Version 1.10 of ais/ai-00051.txt

Unformatted version of ais/ai-00051.txt version 1.10
Other versions for file ais/ai-00051.txt

!standard 13.03 (25)          04-12-08 AI95-00051-01/15
!standard 13.03 (28)
!standard 13.03 (30)
!standard 13.03 (31)
!standard 13.03 (32)
!standard 13.03 (42)
!standard 13.03 (43)
!standard 13.03 (50)
!standard 13.03 (56)
!class binding interpretation 95-06-25
!status Amendment 200Y 04-09-27
!status ARG Approved 8-0-1 04-09-18
!status work item 98-04-01
!status ARG Approved (subject to letter ballot) 8-0-2 97-11-14
!status work item (letter ballot was 4-4-3) 96-10-03
!status letter ballot requested
!status work item (letter ballot was 8-2-1) 96-06-05
!status ARG approved (subject to letter ballot) ??? 95-11-01
!status received 95-06-25
!reference AI95-00291
!priority High
!difficulty Medium
!subject Size and Alignment clauses
!summary
This AI describes the recommended level of support for Size and Alignment clauses for subtypes and for objects.
This AI combines the previous AI-109 (treating subtypes and objects in separate AIs turned out to be a bad idea), but removes discussion of aliased objects (now treated in AI-291).
The following Recommended Level of Support is added:
1. The implementation should support a Size or Alignment clause for an
object if it specifies the same Size or Alignment that the implementation would have chosen by default (that is, a "confirming" representation item, as defined in AI-291).
2. For a signed integer type, the implementation should support an
Alignment clause which specifies a value no larger than the largest Alignment value that would be chosen by the implementation in the absence of an attribute_definition_clause for any signed integer type. Corresponding advice applies for modular integer types, fixed point types, enumeration types, record types, and array types.
3. For a signed integer type, the implementation should support a
Size clause which specifies a value no larger than the largest Size value that would be chosen by the implementation in the absence of an attribute_definition_clause for any signed integer type. Corresponding advice applies for modular integer types, fixed point types, and enumeration types.
4. Only confirming Alignment clauses need be supported for floating point
types, access types, protected types, and task types.
5. Only confirming Size clauses need be supported for floating point
types, access types, record types, arrays types, protected types, and task types.
6. Alignments other than a power-of-two need not be supported.
7. Confirming representation items have no semantic effect (this means
eliminating rules of the form "If attribute Xyz is specified, then" ...)
!question
What is the recommended level of support for Size and Alignment clauses?
For example, the recommended level of support for the Size attribute of an object (in RM-13.3(42,43)) implies that the following should be accepted:
X: Integer; for X'Size use 1024;
Is this the intent? (No).
!recommendation
(See summary.)
!wording
In 13.3(25) replace:
"If the Alignment of a subtype is specified, then the Alignment of an object is
at least as strict, unless the object's Alignment is also specified".
with
"The Alignment of an object is at least as strict as the Alignment
of its subtype, unless the object's Alignment is specified".
Replace 13.3(28) with
"Program execution is erroneous if an object that is not allocated under
control of the implementation is not aligned according to its Alignment".
Replace 13.3(30-32) with:
- An implementation should support a confirming Alignment clause for
any kind of type.
- An implementation should support a nonconfirming Alignment clause
for a discrete type, fixed point type, record type, or array type, specifying an Alignment value that is zero or a power of two, subject to the following:
- An implementation need not support an Alignment clause for a signed
integer type specifying an Alignment greater than the largest Alignment value that is ever chosen by default by the implementation for any signed integer type. A corresponding limitation may be imposed for modular integer types, fixed point types, enumeration types, record types, and array types.
- An implementation need not support a nonconfirming Alignment clause
which could enable the creation of an object of an elementary type which cannot be easily loaded and stored by available machine instructions.
Replace 13.3(42-3) with
The recommended level of support for the Size attribute of objects is the same as for subtypes (see below).
In 13.3(50) replace
"If the Size of a subtype is specified, and allows for independent ..."
with
"If the Size of a subtype allows for independent ..."
.
Insert after 13.3(56) (as part of the bulleted list)
- An implementation should support a confirming Size clause for any
kind of type.
- An implementation should support a nonconfirming Size clause for
a discrete type or a fixed point type, subject to the following:
- An implementation need not support a Size clause for a signed
integer type specifying a Size greater than that of the largest signed integer type supported by the implementation in the absence of a size clause (that is, when the size is chosen by default). A corresponding limitation may be imposed for modular integer types, fixed point types, and enumeration types.
!discussion
The current recommended level of support, interpreted strictly, includes support for absurd constructs. This is the motivation for this AI.
This AI depends on AI-291's definition of "confirming", as well as AI-291's coverage of various cases involving aliased objects and by-reference types.
We "recommend" support for nonconfirming Size and Alignment clauses for a discrete and fixed-point type.
!example
This example shows some ramifications of the Recommended Level of Support. For each representation item, we note whether it is covered by the Recommended Level of Support [Should be supported], whether it might be covered by the Recommended Level of Support (depending on the largest values allowed for integers) [Might be supported], or is not covered by the Recommended Level of Support [Need not be supported].
type Unspecified is range System.Min_Int .. System.Max_Int; -- no Size or Alignment clause
type T1 is range 1 .. 10; for T1'Size use Unspecified'Size; -- Should be supported for T1'Alignment use Unspecified'Alignment; -- Should be supported
type T2 is range 1 .. 10; for T2'Size use 2 * Unspecified'Size; -- Might be supported
type T3 is range 1 .. 10; for T3'Size use 2 * Unspecified'Alignment; -- Might be supported
type T4 is new Integer; for T4'Alignment use 3; -- Need not be supported
!corrigendum 13.3(25)
Replace the paragraph:
Alignment may be specified for first subtypes and stand-alone objects via an attribute_definition_clause; the expression of such a clause shall be static, and its value nonnegative. If the Alignment of a subtype is specified, then the Alignment of an object of the subtype is at least as strict, unless the object's Alignment is also specified. The Alignment of an object created by an allocator is that of the designated subtype.
by:
Alignment may be specified for first subtypes and stand-alone objects via an attribute_definition_clause; the expression of such a clause shall be static, and its value nonnegative. The Alignment of an object is at least as strict as the Alignment of its subtype, unless the object's Alignment is specified. The Alignment of an object created by an allocator is that of the designated subtype.
!corrigendum 13.3(28)
Replace the paragraph:
If the Alignment is specified for an object that is not allocated under control of the implementation, execution is erroneous if the object is not aligned according to the Alignment.
by:
Program execution is erroneous if an object that is not allocated under control of the implementation is not aligned according to its Alignment.
!corrigendum 13.3(30)
Replace the paragraph:
by:
!corrigendum 13.3(31)
Replace the paragraph:
by:
!corrigendum 13.3(32)
Replace the paragraph:
by:
!corrigendum 13.3(42)
Replace the paragraph:
The recommended level of support for the Size attribute of objects is:
by:
The recommended level of support for the Size attribute of objects is the same as for subtypes (see below).
!corrigendum 13.3(43)
Delete the paragraph:
!corrigendum 13.3(50)
Replace the paragraph:
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:
by:
If the Size of a subtype allows for efficient independent addressability (see 9.10) on the target architecture, then the Size of the following objects of the subtype should equal the Size of the subtype:
!corrigendum 13.3(56)
Insert after the paragraph:
the new paragraphs:
!ACATS Test
There are existing "Recommended Level of Support" tests which cover most of this advice. It probably would be worth checking for missing coverage.
!appendix

!section 13.3(42)
!subject Object'Size
!reference RM95-13.3(42,43)
!from Keith Thompson 95-05-10
!reference as: 95-5141.a Keith Thompson 95-5-10>>
!discussion

The recommended level of support for the Size attribute of an
object implies that the following should be accepted:

    X: Integer;
    for X'Size use 1024;

Is this the intent?  If so, what should be done with the extra 922 bits
(if Integer'Size = 32)?  If X is passed as a parameter, must all 1024 bits
be copied?  (Presumably not, since the callee won't be expecting them.)

A possible implementation is to treat only the first 32 of the 1024 bits
as an Integer object, but then the other 922 bits are effectively not
part of X.  It seems more sensible to reject the size clause.  I see
little point in allowing a 'Size for an integer object to exceed the
largest size of any integer type.

Suggestion: for elementary types, the recommended level of support for
the Size attribute should not call for sizes larger than the largest
supported size for the class (discrete, float, access, etc.).

****************************************************************

!section 13.3(42)
!subject Size clauses for objects specifying large sizes
!reference AI95-00051
!reference 13.3(42)
!from Tucker Taft 95-10-30
!reference 95-5373.g Tucker Taft 95-10-30>>
!discussion

This AI talks about base subtypes, which don't exist for
non-scalar types.  In general, when you start talking about
representation items, I find it is better to consider "integral" scalar types
(integer, enum, and fixed), floating point types, access types, array types,
and non-array composite types separately, rather than grouping everything
together.  If the right answer happens to be the same for each of
these distinct groups, great.  But don't be afraid to admit that
different rules are needed for different kinds of types.

As far as this AI, I think its recommendations are reasonable for
"integral" scalar subtypes.

For floating point and access subtypes, I don't think the implementation
needs to accept anything but a "confirming" Size clause.

For composite types, the Size clause on an object presumably just
specifies some amount of padding on allocation, but has no effect on any
other operations on the type, and hence is pretty trivial to support.
Even "block assignment" of a "padded" composite object should not
be affected, since assignment is really defined more in terms of
component-wise assignment, and clearly there are no additional components.
In particular, any gaps in the middle of a composite type don't need
to be assigned, compared, or whatever, as part of a predefined operation on
a composite object.  So clearly padding at the end can be ignored
for these operations as well.

Hence, for composite types, I would suggest that any size that is
a multiple of the storage unit, and is at least as big as the subtype
'Size, be accepted.  It need have no effect other than to add padding
at the end of the object.

****************************************************************

!section 13.3(55)
!subject Missing AI: When is the Size "specified" for a subtype?
!reference RM95-13.3(55)
!from Tucker Taft 95-10-30
!reference 95-5373.b Tucker Taft 95-10-30>>
!discussion

In 13.3(55), it defines a recommended meaning for Size, when it is
not "specified" for a subtype.  Throughout the language, two
statically matching subtypes are considered completely equivalent. Hence,
if a subtype statically matches the first subtype, and the size
has been specified for the first subtype, then Size should be
considered "specified" for the statically matching subtype.
Or to put it another way, all statically matching subtypes should
have the same value for 'Size.

Any other interpretation of the phrase "if not specified" in
13.3(55) would create numerous problems.  In particular, if
one said that specifying a size for a first subtype "broke"
static matching between the first subtype and other subtypes whose
constraints statically match those of the first subtype, then putting
on a "confirming" rep-clause could seriously alter the correctness
of a program.  Alternatively, if one said that only specifying a
non-confirming rep-clause would "break" static matching, then compilers
that differed over what was the default Size for a subtype could have
dramatically different effects with respect to static subtype matching.
(Remember that compilers that do not support the S.P. Annex need
not obey 13.3(55).)

Note that the "Recommended Level of Support" part of the summary becomes
mandatory in the Systems Programming Annex, whereas the "Implementation
Advice" does not.

ASIDE:

    Note that there has been some interest in reviving the ability
    to specify sizes on "secondary" subtypes.  The above suggested
    interpretation for "specified" should not be interpreted to
    affect this possibility either way.  If a secondary subtype
    were to have a Size specified, then we would have to determine
    how such rep-clauses would interact with static matching.
    However, even if such rep-clauses were allowed, we believe that
    when they are *not* present, any subtypes which statically
    match the first subtype must have the same 'Size as the first
    subtype.

END OF ASIDE.

****************************************************************

!section 13.1(14)
!subject Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!from Gary Dismukes 95-11-01
!reference 95-5379.a Gary Dismukes 95-11-2>>
!discussion

(This comment is being submitted on behalf of Robert Dewar.)

Some comments on the SIZE problem and Issue
-------------------------------------------

First of all, let's look at the current rules in the absence of size
clauses:

   type Count is Integer range 1 .. 65;

   subtype Number_Of_Companies is Count range 1 .. 64;
   subtype Number_Of_Hotels    is Count range 1 .. 65;

   type Count_Ptr     is access all Count;
   type Companies_Ptr is access all Number_Of_Companies;
   type Hotels_Ptr    is access all Number_Of_Hotels

We have, according to paragraph 13.3(55):

   Count'Size = 7
   Number_Of_Companies'Size = 6;
   Number_Of_Hotels'Size = 7;

This is required. In Ada 83, many (most? nearly all?) compilers would
have given Integer'Size to all three types, but Ada 95 requires that
the sizes be minimal. This causes a number of subtle incompatibilities
but we already (unwisely in my view, I will not hide this) decided to
introduce this behavior.

Moreover, type Count_Ptr is convertible to type Hotels_Ptr, but
not to type Companies_Ptr.

This again seems very strange, but that's the way the language is,
and for this to work, we need to worry about:

   X : aliased Count_Ptr;
   Y : aliased Hotels_Ptr;

where X'Access and Y'Access must be interconvertible. This means that
X and Y must be stored using the same representation.

In the absence of Size clauses, Ada 95 achieves this requirement by
the default size rules in 13.3(55), and in this case 13.1(14) clearly
is correct:

  "If two subtypes statically match, then their subtype-specific
   aspects (Size and Alignment) are the same"

Together with the assumption (perhaps it is a requirement, but I
can't see the reference right now and anyway it is not really
relevant to this argument which it is) that the two objects will
be stored the same way if the sizes are the same.

What Happens When we Add Size Clauses
-------------------------------------

13.3(55) allows a size to be specified for the first subtype. If this
is a confirming size, then all is well, but consider in the above
example what happens when we write:

   type Count is Integer range 1 .. 65;

   subtype Number_Of_Companies is Count range 1 .. 64;
   subtype Number_Of_Hotels    is Count range 1 .. 65;

   for Count'Size use 16;

   type Count_Ptr     is access all Count;
   type Companies_Ptr is access all Number_Of_Companies;
   type Hotels_Ptr    is access all Number_Of_Hotels

A straightforward reading of 13.3(55) would clearly suggest that the
size clause affects only the size of Count, and that we would have:

   Count'Size = 16;
   Number_Of_Companies'Size = 6;
   Number_Of_Hotels'Size = 7;

But now we are in trouble, the subtypes Count and Number_Of_Hotels
still appear to staticaly match, but 13.1(14) is violated. So
something is wrong.

There are two basic approaches one can take to solving this problem:

  Tuck's Approach

    Hereinafter referred to as TA

    TA reads 13.1(14) as a requirement rather than an observation,
    even though it does not have a shall in it. So the effect of 13.1(14)
    in TA is that if the other rules on static subtypes are met, then
    the Size and Alignment SHALL be the same.

    TA reads into the last sentence of 13.3(55) the fact that if you
    specify the size for the first named subtype, then you also are
    implicitly specifying the size for any statically matching
    subtypes (because of the requirement of 13.1(14), and that
    therefore the default ("if not specified") size rule does not
    apply.

    Using TA, in the above example, with the size clause, the
    value of Number_Of_Hotels'Size would become 16.

  Dewar's approach

    Hereinafter referred to as DA

    Does not treat 13.1(14) as a requirement, but rather a statement
    about what is true about static subtypes. In other words, if this
    is not true, then the subtypes do not statically match. Roughly
    the view is that if you state a rule such as:

      "Identical twins always have the same sex"

    and then you look at a boy and a girl, you know they are not identical
    twins, no matter what rules have been stated elsewhere.

    DA then takes the more natural reading of 13.3(55), and assumes that
    the size clause applies only to the first named subtype.

  Tuck mentions a third approach as a strawman in his comments on this
  subject, but (a) I don't understand it, and (b) as far as I can tell
  no one is arguing for it, so I won't bother with it.

Now, some comments on the two approaches:

1. Both TA and DA allow confirming rep clauses to be added for the first
   subtype without modifying the behavior in any way. This is important,
   since apparently one of Tuck's main concerns is that confirming rep
   clauses not have any semantic effect.

   (I agree that confirming rep clauses should not have any effect, but in
   the case of Size, this principle is so badly compromised by the inability
   to give any rep clauses for subtypes at all, not even confirming rep
   clauses, that I can't get too excited about it, but in any case, DA
   allows confirming rep clauses that are neutral).

2. DA means that a non-confirming size clause can affect the convertability
   of pointers to different subtypes. This is presumably the overwhelming
   concern that leads to the preference of TA.

   However, I must say I am underwhelmed. First of all it is a bit bizarre
   that the legality of these conversions depends on the equality of values
   that are likely to be logically related in the program (for example in
   the above case, if you open a new company, you suddenly get added
   convertability from Count_Ptr to Companies_Ptr. That's unlikely of
   course to cause trouble in an existing program. If you remove a hotel,
   then you lose convertability. This could cause trouble, but in fact is
   very unlikely to do so, since the conversion from Count_Ptr to
   Hotels_Ptr is a bizarre one (and the conversion from Hotels_Ptr to
   Companies_Ptr, which is now allowed is even more bizarre).

   Furthermore, it seems quite natural to me that if you have two types:

      access X
      access Y

   that they are convertible only if the representations of X and Y are
   the same, and if you specifically modify the representation of X and
   do not modify the representation of Y, then they are no longer
   convertible.

3. TA means that a size clause affects the size of a previously declared
   subtype. This seems peculiar, and I can see it causing implementation
   problems. For non subtype specific attributes, there is no problem,
   since the representation is an attribute of the base type.

   DA does not at all like this retrospective effect.

   TA does not care much, since one pass semantics aren't considered too
   important, and anyway that's not quite fair, because you can think of
   sizes as only being established at the freeze point.

4. DA thinks that it is strange that if you specify the size of the first
   subtype, then it has an effect on a certain subset of subtypes, those
   which happen to statically match, and that saying that this is the case
   will complicate 13.3(55). For example, TA could be made clear by adding
   to this paragraph the following sentence:

     Such a specification affects the first named subtype, and any other
     subtypes of the same base type that statically match.

   Probably a note needs to be added:

     This affects both subtypes declared previously to the size clause,
     and any subsequent subtypes declared in either the same declarative
     region or elsewhere.

   because this is quite surprising (that the size of a subtype in another
   unit would be affected). The actual processing for subtypes in other
   units is now something like:

     If the subtype statically matches the first named subtype, then take
     the size of the first named subtype, otherwise take the default size
     from paragraph 13.3(55).

   By contrast, the effect of DA on the RM would be as follows:

     Remove 13.1(14) completely

     Add to the first sentence of 4.9.1(2), giving

     "A subtype statically matches another subtype of the same type if they
      have statically matching constraints, and if they have the same
      subtype specific aspects (Size and Alignment)".

What we have here is two conflicting requirements in the RM. I think in such
cases it is a waste of time to make arguments saying essentially that you
can conclude that one is wrong by intent because it must be wrong, given
the other requirement. This argument can be applied either way round and
is unhelpful. I mention this, because TA uses this argument, arguing that
13.1(14) implies the desired constructive reading of 13.3(55). DA could
make the same argument the other way round, but it is besides the point.

What we need is a resolution that is least suprising, and simplest.

Ulterior Motive Disclosed
-------------------------

DA will now disclose an ulterior motive.

Up until the last moment, Ada 95 allowed 'Size to be specified for subtypes.
GNAT implemented this capability, and it was definitely useful. It was
removed from the RM late on without WG9 discussion.

Why?

Because of these static matching rules. The attempt was to ensure that
13.1(14) is in fact true, and that specifying sizes for subtypes could
not disrupt it.

But that did not get done cleanly as we see.

To me, the removal of a really useful feature, just so that the very
marginal feature of being able to convert pointers to logically unrelated
subtypes would work, is a very poor trade off.

It is particularly important to be able to specify the size of subtypes
given the (in my view injudicious) introduction of the rules in Ada 95
that require separate sizes for subtypes (Ada 83 allowed it but did not
require it, few compilers took advantage of it, the only one I know of
that did systematically what is now required is the old Intermetrics
compiler).

This means that if we have

   type X is range 0 .. 65535;
   for X'Size use 16;

   subtype Y is X range 1 .. N;
   -- N is a constant that happens today to be 255

   then Y'Size is 8, and that in the record:

      type Rec is record
         XV : X;
         YV : Y;
      end record;

   XV is likely to occupy 16 bits, and YV to occupy 8 bits. This could well
   be incompatible with existing programs, and note that it would require
   most vendors to change their size rules, and behavior of their systems
   (with the sole exception of the Intermetrics compiler, which is indeed
   compatible with the old Intermetrics compiler).

This was not *too* worrisome before the last minute change, since at least
you could use a size clause (in this case "for Y'size use X'Size) that
would override the default, and duplicate old behavior.

Furthermore, the introduction of the restriction means that it is not
possible to specify confirming size clauses. So you have a situation in
which compared to Ada 83, the sizes of subtypes are required to vary in
an unexpected manner, and there is no way to either confirm or change
this behavior.

DA has the very interesting property that if it is adopted, then there is
no reason not to allow compilers to specify the size of subtypes, undoing
the ill effects of the last minute, incompletely executed, change.

A comment here is that MANY readers of Ada 95 assume that subtype specific
attributes can be specified for subtypes. That seems a reasonable assumption
and used to be a correct one, but is now surprisingly false. We have got a
number of bug reports from people complaining that GNAT does not allow such
specifications. We can of course explain that GNAT is right, but the
principle of least suprise is definitely violated!

Incidentally, a major use for specifying subtype sizes is in building
packed records. You just specify the size of each subtype, and then say
pragma Pack, and everything packs together nicely without needing to
write a (possibly non-portable) representation clause. Furthermore
the representation clause here is clearly undesirable over-specification.
You don't want to specify the exact layout, but you do want to control
the nature of the packing. Yes there are other ways to do this, e.g.
by using derived types, but they are messy by comparison.

The other major reason for specifying subtype sizes is in porting legacy
code, where you want to confirm size choices made by a previous Ada 83
compiler.

These requirements are strong enough that in GNAT we definitely intend to
implement a feature for this purpose.

If TA is adopted, GNAT will introduce an attribute Subtype_Size that can
be applied to arbitrary subtypes, and which will compromise static matching
in some cases. This attribute is probably technically an extension because
of this compromising effect, so we will put it under the non-pedantic (GNU'ese
for extensions allowed) mode.

If DA is adopted, GNAT will simply allow 'Size for subtypes.

A note for Bob Duff
-------------------

Yes, yes, all this applies to Alignment too. I can't get excited about
alignments for subtypes. At least there is no analog to 13.3(55) requiring
an injudicious choice of default alignments.

To be Honest
------------

There are a couple more glitches here

First, with respect to dynamic subtypes. We have to worry about dynamic
subtypes, since they can statically match. For dynamic subtypes, if you
want to be sure that access types to them are inter-convertible, then
you have to read 13.1(14) as a requirement that the sizes of statically
matching dynamic subtypes match. Note that there are no rules on the
default sizes for dynamic subtypes. TA of course handles this fine, by
changing the "are" in 13.1(14) to "shall".

DA can't get very excited about this problem, and doesn't really care
if convertability of such access types is impl dependent, but, if pressed,
would be inclined to require that all dynamic subtypes have the size
of the base type. This avoids differences between implementations, and
is perfectly acceptable in practice.

Second, with respect to alignments, there is no clear statement that requires
choice of alignments. TA would say that 13.1(14) places the requirement that
if two subtypes statically match then the alignments should match. Again
DA can't get excited, but if pressed would be inclined to say that the
alignments of two subtypes of the same size should be identical.

Original Intent
---------------

Arguments from original intent are somewhat bogus, since the RM does not
contain its history.

One could say that the longer term intent was that Size should be able
to be specified for subtypes, and that the language "subtype specific"
still implies this -- a bogus argument for DA.

One could also say that the removal of this capability obviously means
that 13.1(14) was considered sacrosanct -- a bogus argument for TA.

Conclusion
----------

There is a real disagreement here. Needs discussion!

****************************************************************

!section 13.1(14)
!subject 'SIZE problem violates Ada95 "no surprises" issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 95-5379.a Gary Dismukes 95-11-2
!from David Emery 95-11-02
!reference 95-5382.a David_Emery_at_1-BCCDFA@CCGATE.HAC.COM 95-11-3>>
!discussion

RBKD's example:

   type X is range 0 .. 65535;
   for X'Size use 16;

   subtype Y is X range 1 .. N;
   -- N is a constant that happens today to be 255

   then Y'Size is 8, and that in the record:

      type Rec is record
         XV : X;
         YV : Y;
      end record;
led me to observe that the following:
    package Rec_IO is new Direct_IO (Rec);
would cause real problems when a database is maintained using
Rec_IO bt different invocations of the program, with different
values of N.    The expectation (guaranteed by Ada83 because Y
is a subtype and not a type, I beleive), is that objects of
type Rec are the same size.  This is a particular issue for
Direct_IO, where the data layouts can depend very strongly on
the size of the object.

Ada95 clearly violates its "no surprises" guideline by having
the size of a subtype change in this way.  I find it basically
counterintuitive (and unacceptable) to have to do a lot of
representation clauses for an 'invariant' that should be machine
independent, i.e. that objects of the type Rec are the same
size, regardless of the value of the constraint N.  In particular,
if this causes Direct_IO to fail because of varying values of N,
then we lose type 'equivalence', exchanging it for subtype
equivalence.  (What I mean here is that 2 values of the same type
can be used, subject only to subtype constraint checks.)

                dave

****************************************************************

!section 13.1(14)
!subject Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 95-5379.a Gary Dismukes 95-11-2
!from Bob Duff
!reference 96-5462.a Robert A Duff 96-4-11>>
!discussion

Robert, you sent in this comment just before the last ARG meeting.
It was discussed at the meeting, with no resolution of the issues.
There are some confusing things.  Could you please send a
clarification?

> (This comment is being submitted on behalf of Robert Dewar.)
>
> Some comments on the SIZE problem and Issue
> -------------------------------------------
>
> First of all, let's look at the current rules in the absence of size
> clauses:
>
>    type Count is Integer range 1 .. 65;

That's not legal syntax.  You must mean one of these:

    type Count is range 1 .. 65;
    subtype Count is Integer range 1 .. 65;
    type Count is new Integer range 1 .. 65;

The ARG discussion got confused about this.

>    subtype Number_Of_Companies is Count range 1 .. 64;
>    subtype Number_Of_Hotels    is Count range 1 .. 65;

There was some feeling at the ARG meeting that this is not a compelling
example, because you're using the same type (two different subtypes) for
two totally unrelated things.  If this causes surprises, tough luck --
you should have used two different types in the first place.

Now, I realize there are (at least) two schools of thought -- some
people like to use lots of different types, whereas some people like to
make everything a subtype of the same type.  Anyway, you weren't there
to defend yourself, so you might want to do so now.

>    type Count_Ptr     is access all Count;
>    type Companies_Ptr is access all Number_Of_Companies;
>    type Hotels_Ptr    is access all Number_Of_Hotels
>
> We have, according to paragraph 13.3(55):
>
>    Count'Size = 7
>    Number_Of_Companies'Size = 6;
>    Number_Of_Hotels'Size = 7;
>
> This is required. In Ada 83, many (most? nearly all?) compilers would
> have given Integer'Size to all three types, but Ada 95 requires that
> the sizes be minimal. This causes a number of subtle incompatibilities
> but we already (unwisely in my view, I will not hide this) decided to
> introduce this behavior.
>
> Moreover, type Count_Ptr is convertible to type Hotels_Ptr, but
> not to type Companies_Ptr.
>
> This again seems very strange, but that's the way the language is,
> and for this to work, we need to worry about:
>
>    X : aliased Count_Ptr;
>    Y : aliased Hotels_Ptr;
>
> where X'Access and Y'Access must be interconvertible. This means that
> X and Y must be stored using the same representation.
>
> In the absence of Size clauses, Ada 95 achieves this requirement by
> the default size rules in 13.3(55), and in this case 13.1(14) clearly
> is correct:
>
>   "If two subtypes statically match, then their subtype-specific
>    aspects (Size and Alignment) are the same"
>
> Together with the assumption (perhaps it is a requirement, but I
> can't see the reference right now and anyway it is not really
> relevant to this argument which it is) that the two objects will
> be stored the same way if the sizes are the same.

I don't think there's an explicit requirement, but it seems that this is
the only viable run-time model -- aliased objects have to be stored the
same way if their type is the same, and their subtypes statically match.
The compiler can play all kinds of games with non-aliased objects.

> What Happens When we Add Size Clauses
> -------------------------------------
>
> 13.3(55) allows a size to be specified for the first subtype. If this
> is a confirming size, then all is well, but consider in the above
> example what happens when we write:
>
>    type Count is Integer range 1 .. 65;

Same bug as above.

>    subtype Number_Of_Companies is Count range 1 .. 64;
>    subtype Number_Of_Hotels    is Count range 1 .. 65;
>
>    for Count'Size use 16;
>
>    type Count_Ptr     is access all Count;
>    type Companies_Ptr is access all Number_Of_Companies;
>    type Hotels_Ptr    is access all Number_Of_Hotels
>
> A straightforward reading of 13.3(55) would clearly suggest that the
> size clause affects only the size of Count, and that we would have:
>
>    Count'Size = 16;
>    Number_Of_Companies'Size = 6;
>    Number_Of_Hotels'Size = 7;
>
> But now we are in trouble, the subtypes Count and Number_Of_Hotels
> still appear to staticaly match, but 13.1(14) is violated. So
> something is wrong.
>
> There are two basic approaches one can take to solving this problem:
>
>   Tuck's Approach
>
>     Hereinafter referred to as TA
>
>     TA reads 13.1(14) as a requirement rather than an observation,
>     even though it does not have a shall in it. So the effect of 13.1(14)
>     in TA is that if the other rules on static subtypes are met, then
>     the Size and Alignment SHALL be the same.

There are lots of requirements that have no "shall".  Legality Rules use
"shall", but Static Semantics just state "facts" about the language --
in this case, the sizes "are" the same.  The facts are of course
requirements on the implementation -- if they're not true in a given
implementation, then that's not a correct implementation of Ada.
My point is: Don't base your argument on whether "shall" was used.

I do admit we have a problem, here.

>     TA reads into the last sentence of 13.3(55) the fact that if you
>     specify the size for the first named subtype, then you also are
>     implicitly specifying the size for any statically matching
>     subtypes (because of the requirement of 13.1(14), and that
>     therefore the default ("if not specified") size rule does not
>     apply.
>
>     Using TA, in the above example, with the size clause, the
>     value of Number_Of_Hotels'Size would become 16.
>
>   Dewar's approach
>
>     Hereinafter referred to as DA
>
>     Does not treat 13.1(14) as a requirement, but rather a statement
>     about what is true about static subtypes. In other words, if this
>     is not true, then the subtypes do not statically match. Roughly
>     the view is that if you state a rule such as:
>
>       "Identical twins always have the same sex"
>
>     and then you look at a boy and a girl, you know they are not identical
>     twins, no matter what rules have been stated elsewhere.

No, that's not right.  If the RM said that, we would have written "An
identical twins is a pair of siblings that have the same sex [...and a
bunch of other stuff]".  Or, "NOTE: Note that identical twins always
have the same sex." (presuming it follows from the definition).

I wrote 13.1(14), and it's written in the same style as all the other
Static Semantics, and it means that we are *requiring* this to be true
in all implementations.

>     DA then takes the more natural reading of 13.3(55), and assumes that
>     the size clause applies only to the first named subtype.
>
>   Tuck mentions a third approach as a strawman in his comments on this
>   subject, but (a) I don't understand it, and (b) as far as I can tell
>   no one is arguing for it, so I won't bother with it.
>
> Now, some comments on the two approaches:
>
> 1. Both TA and DA allow confirming rep clauses to be added for the first
>    subtype without modifying the behavior in any way. This is important,
>    since apparently one of Tuck's main concerns is that confirming rep
>    clauses not have any semantic effect.
>
>    (I agree that confirming rep clauses should not have any effect, but in
>    the case of Size, this principle is so badly compromised by the inability
>    to give any rep clauses for subtypes at all, not even confirming rep
>    clauses, that I can't get too excited about it, but in any case, DA
>    allows confirming rep clauses that are neutral).
>
> 2. DA means that a non-confirming size clause can affect the convertability
>    of pointers to different subtypes. This is presumably the overwhelming
>    concern that leads to the preference of TA.
>
>    However, I must say I am underwhelmed. First of all it is a bit bizarre
>    that the legality of these conversions depends on the equality of values
>    that are likely to be logically related in the program (for example in
>    the above case, if you open a new company, you suddenly get added
>    convertability from Count_Ptr to Companies_Ptr. That's unlikely of
>    course to cause trouble in an existing program. If you remove a hotel,
>    then you lose convertability. This could cause trouble, but in fact is
>    very unlikely to do so, since the conversion from Count_Ptr to
>    Hotels_Ptr is a bizarre one (and the conversion from Hotels_Ptr to
>    Companies_Ptr, which is now allowed is even more bizarre).

This is where the two schools of thought I mentioned make a difference
-- many folks think that you would make Number_Of_Hotels and
Number_Of_Companies two different types, so convertibility changes would
not happen.

>    Furthermore, it seems quite natural to me that if you have two types:
>
>       access X
>       access Y
>
>    that they are convertible only if the representations of X and Y are
>    the same, and if you specifically modify the representation of X and
>    do not modify the representation of Y, then they are no longer
>    convertible.
>
> 3. TA means that a size clause affects the size of a previously declared
>    subtype. This seems peculiar, and I can see it causing implementation
>    problems. For non subtype specific attributes, there is no problem,
>    since the representation is an attribute of the base type.
>
>    DA does not at all like this retrospective effect.
>
>    TA does not care much, since one pass semantics aren't considered too
>    important, and anyway that's not quite fair, because you can think of
>    sizes as only being established at the freeze point.

Note that this "retrospective" effect happens only within a single
package spec or declarative part.

Now wait a minute, is this really right?  Don't freezing rules require
you to give the size clause before any other *constrained* subtypes?

> 4. DA thinks that it is strange that if you specify the size of the first
>    subtype, then it has an effect on a certain subset of subtypes, those
>    which happen to statically match, and that saying that this is the case
>    will complicate 13.3(55). For example, TA could be made clear by adding
>    to this paragraph the following sentence:
>
>      Such a specification affects the first named subtype, and any other
>      subtypes of the same base type that statically match.
>
>    Probably a note needs to be added:
>
>      This affects both subtypes declared previously to the size clause,
>      and any subsequent subtypes declared in either the same declarative
>      region or elsewhere.
>
>    because this is quite surprising (that the size of a subtype in another
>    unit would be affected). The actual processing for subtypes in other
>    units is now something like:
>
>      If the subtype statically matches the first named subtype, then take
>      the size of the first named subtype, otherwise take the default size
>      from paragraph 13.3(55).
>
>    By contrast, the effect of DA on the RM would be as follows:
>
>      Remove 13.1(14) completely
>
>      Add to the first sentence of 4.9.1(2), giving
>
>      "A subtype statically matches another subtype of the same type if they
>       have statically matching constraints, and if they have the same
>       subtype specific aspects (Size and Alignment)".
>
> What we have here is two conflicting requirements in the RM. I think in such
> cases it is a waste of time to make arguments saying essentially that you
> can conclude that one is wrong by intent because it must be wrong, given
> the other requirement. This argument can be applied either way round and
> is unhelpful. I mention this, because TA uses this argument, arguing that
> 13.1(14) implies the desired constructive reading of 13.3(55). DA could
> make the same argument the other way round, but it is besides the point.
>
> What we need is a resolution that is least suprising, and simplest.
>
> Ulterior Motive Disclosed
> -------------------------
>
> DA will now disclose an ulterior motive.
>
> Up until the last moment, Ada 95 allowed 'Size to be specified for subtypes.
> GNAT implemented this capability, and it was definitely useful. It was
> removed from the RM late on without WG9 discussion.
>
> Why?
>
> Because of these static matching rules. The attempt was to ensure that
> 13.1(14) is in fact true, and that specifying sizes for subtypes could
> not disrupt it.
>
> But that did not get done cleanly as we see.

This history is correct.

> To me, the removal of a really useful feature, just so that the very
> marginal feature of being able to convert pointers to logically unrelated
> subtypes would work, is a very poor trade off.
>
> It is particularly important to be able to specify the size of subtypes
> given the (in my view injudicious) introduction of the rules in Ada 95
> that require separate sizes for subtypes (Ada 83 allowed it but did not
> require it, few compilers took advantage of it, the only one I know of
> that did systematically what is now required is the old Intermetrics
> compiler).
>
> This means that if we have
>
>    type X is range 0 .. 65535;
>    for X'Size use 16;
>
>    subtype Y is X range 1 .. N;
>    -- N is a constant that happens today to be 255

I presume that N is a *static* constant.  Dave Emery sent a note that
assumed N is non-static, which I think confuses the issue.

>    then Y'Size is 8, and that in the record:
>
>       type Rec is record
>          XV : X;
>          YV : Y;
>       end record;
>
>    XV is likely to occupy 16 bits, and YV to occupy 8 bits. This could well
>    be incompatible with existing programs, and note that it would require
>    most vendors to change their size rules, and behavior of their systems
>    (with the sole exception of the Intermetrics compiler, which is indeed
>    compatible with the old Intermetrics compiler).
>
> This was not *too* worrisome before the last minute change, since at least
> you could use a size clause (in this case "for Y'size use X'Size) that
> would override the default, and duplicate old behavior.
>
> Furthermore, the introduction of the restriction means that it is not
> possible to specify confirming size clauses. So you have a situation in
> which compared to Ada 83, the sizes of subtypes are required to vary in
> an unexpected manner, and there is no way to either confirm or change
> this behavior.

However, note that Ada 95 allows Size clauses on *objects* and
Component_Size clauses for array components, which somewhat alleviates
the problem.  Also, if you really want a Size clause on a subtype, you
can make it a derived type instead.  Not ideal, since extra type
conversion will be needed, but it does work OK.

> DA has the very interesting property that if it is adopted, then there is
> no reason not to allow compilers to specify the size of subtypes, undoing
> the ill effects of the last minute, incompletely executed, change.
>
> A comment here is that MANY readers of Ada 95 assume that subtype specific
> attributes can be specified for subtypes. That seems a reasonable assumption
> and used to be a correct one, but is now surprisingly false. We have got a
> number of bug reports from people complaining that GNAT does not allow such
> specifications. We can of course explain that GNAT is right, but the
> principle of least suprise is definitely violated!
>
> Incidentally, a major use for specifying subtype sizes is in building
> packed records. You just specify the size of each subtype, and then say
> pragma Pack, and everything packs together nicely without needing to
> write a (possibly non-portable) representation clause. Furthermore
> the representation clause here is clearly undesirable over-specification.
> You don't want to specify the exact layout, but you do want to control
> the nature of the packing. Yes there are other ways to do this, e.g.
> by using derived types, but they are messy by comparison.
>
> The other major reason for specifying subtype sizes is in porting legacy
> code, where you want to confirm size choices made by a previous Ada 83
> compiler.
>
> These requirements are strong enough that in GNAT we definitely intend to
> implement a feature for this purpose.
>
> If TA is adopted, GNAT will introduce an attribute Subtype_Size that can
> be applied to arbitrary subtypes, and which will compromise static matching
> in some cases. This attribute is probably technically an extension because
> of this compromising effect, so we will put it under the non-pedantic (GNU'ese
> for extensions allowed) mode.

No, I don't think you need to play the "-pedantic" game.
Implementation-defined attributes can do whatever you like.
The only barrier is the wisdom and good taste of the compiler
writer.  ;-)

> If DA is adopted, GNAT will simply allow 'Size for subtypes.

No, *that's* not allowed.  Unless, of course, the ARG explicitly rules
that it *is* allowed.

> A note for Bob Duff
> -------------------
>
> Yes, yes, all this applies to Alignment too. I can't get excited about
> alignments for subtypes. At least there is no analog to 13.3(55) requiring
> an injudicious choice of default alignments.

And of course for Alignment there's no issue of compatibility with
existing Ada 83 compilers.

We can decide what the right thing for Size is, and then simply rule
that Alignment works the same way, which I think is necessary.

> To be Honest
> ------------
>
> There are a couple more glitches here
>
> First, with respect to dynamic subtypes. We have to worry about dynamic
> subtypes, since they can statically match. For dynamic subtypes, if you
> want to be sure that access types to them are inter-convertible, then
> you have to read 13.1(14) as a requirement that the sizes of statically
> matching dynamic subtypes match. Note that there are no rules on the
> default sizes for dynamic subtypes. TA of course handles this fine, by
> changing the "are" in 13.1(14) to "shall".

No, that's not the correct wording for TA.  As I said before, the Static
Semantics *never* say "shall", they always say "are", and there's no
reason for this case to be any different.  But that's a wording issue.

> DA can't get very excited about this problem, and doesn't really care
> if convertability of such access types is impl dependent, but, if pressed,
> would be inclined to require that all dynamic subtypes have the size
> of the base type. This avoids differences between implementations, and
> is perfectly acceptable in practice.

Well, dynamic subtypes are probably fairly rare.  Nonetheless, I think
it is desirable to *allow* a compiler to deduce a smaller size than the
base SUBtype, for dynamic subtypes.

> Second, with respect to alignments, there is no clear statement that requires
> choice of alignments. TA would say that 13.1(14) places the requirement that
> if two subtypes statically match then the alignments should match. Again
> DA can't get excited, but if pressed would be inclined to say that the
> alignments of two subtypes of the same size should be identical.

I'm not sure what you mean by not getting excited, but clearly, if your
compiler doesn't obey this, then type conversions of access types simply
won't work.

> Original Intent
> ---------------
>
> Arguments from original intent are somewhat bogus, since the RM does not
> contain its history.
>
> One could say that the longer term intent was that Size should be able
> to be specified for subtypes, and that the language "subtype specific"
> still implies this -- a bogus argument for DA.
>
> One could also say that the removal of this capability obviously means
> that 13.1(14) was considered sacrosanct -- a bogus argument for TA.

I agree that arguments about "intent" are often bogus.  The language
designer doesn't always have an accurate memory of intent.  Of course,
in many cases, there is evidence in the AARM about the intent.  In
*this* case, the intent was self contradictory -- the language designers
intended to support Size clauses on non-first subtypes, and also
intended that static matching for static subtypes work as it does, and
also intended that low-level mucking with rep clauses should not affect
the high-level pristine semantics of static matching.  Clearly, one
cannot achieve all of that "intent", so something's got to give.  At the
last minute, we chose to ditch size clauses on non-first subtypes.

> Conclusion
> ----------
>
> There is a real disagreement here. Needs discussion!

Indeed.

- Bob

****************************************************************

!section 13.1(14)
!subject Size attribute
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!from Bob Duff
!reference 96-5463.a Robert A Duff 96-4-11>>
!discussion

Here are some general comments on the Size issue.

First of all, in all these discussions, PLEASE make sure it's very clear
whether you're talking about a query of the Size attribute, or a Size
clause.  Numerous times, discussions on this issue have become mired in
confusion, because I said something about the query, when Robert thought
I was talking about the clause, or vice versa.

Also, please be sure to distinguish the Size of a subtype from the Size
of an object, and the corresponding clauses from each other.  The
semantics are rather different -- the Size of a subtypes is rather
flaky, whereas the Size of an object is pretty well defined.

One principle that most people seem to like is that a "confirming rep
clause" should not change things.  That is, if you have a program where
T'Size = 32 by default, and you add a rep clause, "for T'Size use 32;",
then the semantics of the new program should be identical.

For most rep clauses, in most situations, we have achieved this
principle.  However, there are some cases where the principle is
compromised.  In particular 13.3(50-52) says:

  50   If the Size of a subtype is specified, and allows for efficient
  independent addressability (see 9.10) on the target architecture, then the
  Size of the following objects of the subtype should equal the Size of the
  subtype:

     51  Aliased objects (including components).

     52  Unaliased components, unless the Size of the component is
         determined by a component_clause or Component_Size clause.

The phrase "If the Size of a subtype is specified" clearly violates the
principle that confirming rep clauses shouldn't change things.  The
principle is still a good principle -- we probably ought to at least try
to minimize the cases where it is compromised.

Note well para 51.  The Size of a stand-alone object matters to a
programmer if the programmer takes the Address of that object, and
passes that address off to some other language.  The address ought to be
pointing at a sequence of bytes of the expected size.  The reason para
51 says "aliased" is that 'Address is only well-defined in the case of
aliased objects, by 13.3(16):

   16  X'Address should produce a useful result if X is an object that
       is aliased or of a by-reference type, or is an entity whose
       Address has been specified.

Thus, if you say:

    X: Integer; -- not aliased
    ...X'Address ...

the compiler might put X in a register, and return a null address for
X'Address (or even raise an exception).

In thinking about Size, note that there can be subtypes whose range is
wider than the first subtype:

    type T is range 0..15; -- T'Size = 4
    subtype S is T'Base range -15..15; -- S'Size = 5

The above subtype S is guaranteed to be supported on all
implementations.  My point is that a rule requiring the size of all
subtypes to be the same as the first subtype just won't work.  For
example, even if you don't agree that T'Size should be 4 by default, one
could legally add "for T'Size use 4;" to the above -- certainly that was
legal in Ada 83, and it had better still be legal.  But that Size clause
clearly cannot mean T'Base'Size = 4, nor that S'Size = 4.

Robert and I seem to disagree on the desired semantics of packed
records.  This makes a difference to the Size clause issue, so I think
the ARG should explicitly consider what is desirable for packed records.
(I should note that one Ada 83 compiler doesn't support packed records
at all.  Robert is correct that it would be nice to be compatible with
that compiler, but if it conflicts with the way packed records have to
work, it's not suprising to me that this compatibility is impossible to
achieve).

Here's my view of packed records.  Assume a 32-bit machine with 8-bit
bytes.

    type My_Boolean is new Boolean; -- My_Boolean'Size = 1.
    type My_Other_Boolean is (No, Yes); -- My_Other_Boolean'Size = 1.
    subtype One_Bit_Int is Integer range 0..1; -- One_Bit_Int'Size = 1.

    type Inner_Record is
        record
            X: My_Boolean;
            Y: My_Other_Boolean;
            Z: One_Bit_Int;
        end record;
    pragma Pack(Inner_Record);

    type Twenty_Nine_Bits is range 0..2**29-1; -- Twenty_Nine_Bits'Size = 29.

    type Outer_Record is
        record
            Inner: Inner_Record;
            W: Twenty_Nine_Bits;
        end record;
    pragma Pack(Outer_Record);

To me, it seems highly desirable that the Size values shown above should
be chosen by default, and that Outer_Record should fit in a single
32-bit word, and that Outer_Record'Size should be 32.

My understanding is that 13.2(8) requires this:

    8  For a packed record type, the components should be packed as
       tightly as possible subject to the Sizes of the component
       subtypes, and subject to any record_representation_clause that
       applies to the type; the implementation may, but need not,
       reorder components or cross aligned word boundaries to improve
       the packing.  A component whose Size is greater than the word
       size may be allocated an integral number of words.

I believe Robert's view (please correct me if I'm wrong) is that the
programmer should have to write some Size clauses, in order to achieve
the above tight packing.  For example, the user would have to write:

    for One_Bit_Int'Size use 1;
    for Twenty_Nine_Bits'Size use 29;

I'm not sure if Robert's view also requires:

    for My_Boolean'Size use 1;
    for My_Other_Boolean'Size use 1;

in addition.  If so, that's pretty strange, because Boolean'Size = 1
(even though Boolean has no representation clause in Standard).  If not,
that's even stranger, since I would expect integers and enumerations to
behave the same way in this regard.

In any case, I don't see why the user should have to write Size clauses
to get reasonable packing.  That defeats the purpose of pragma Pack.  I
don't care to specify all kinds of details -- I just want the compiler
to choose a reasonably-packed representation.  (If I *did* care about
details, I would use a record_rep_clause instead.)

Certainly, in the Pascal compilers I've used, packed records are packed
according to the sub-ranges declared, without any need for Size clauses
(which is lucky, since Pascal doesn't have Size clauses).

Now, one could imagine that the record is packed like I want it, but the
'Size values are not as I indicated above.  I don't see how this can
work.  The very meaning of 'Size is that there are no objects smaller
than that (barring other overriding rep clauses).  Also, we don't want
to say that packing is different depending on whether the Size was
specified or chosen by default.

I think Robert's view is that requiring the Size clauses is desirable,
because then the desired packing won't silently change if you change the
bounds of Twenty_Nine_Bits, for example -- you'll either get the same
package, or you'll get an error message on the Size clause.  Obviously,
I don't agree that this is desirable.

- Bob

****************************************************************

!section 13.1(14)
!subject Size attribute
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5463.a Robert A Duff 96-4-11
!from Keith Thompson 96-04-12
!reference 96-5466.a Keith Thompson 96-4-12>>
!discussion

Bob Duff writes:
> One principle that most people seem to like is that a "confirming rep
> clause" should not change things.  That is, if you have a program where
> T'Size = 32 by default, and you add a rep clause, "for T'Size use 32;",
> then the semantics of the new program should be identical.
>
> For most rep clauses, in most situations, we have achieved this
> principle.  However, there are some cases where the principle is
> compromised.  In particular 13.3(50-52) says:
>
>   50   If the Size of a subtype is specified, and allows for efficient
>   independent addressability (see 9.10) on the target architecture, then the
>   Size of the following objects of the subtype should equal the Size of the
>   subtype:
>
>      51  Aliased objects (including components).
>
>      52  Unaliased components, unless the Size of the component is
>          determined by a component_clause or Component_Size clause.
>
> The phrase "If the Size of a subtype is specified" clearly violates the
> principle that confirming rep clauses shouldn't change things.  The
> principle is still a good principle -- we probably ought to at least try
> to minimize the cases where it is compromised.

In most cases, this doesn't necessarily violate the principle; at
worst, it's an incomplete statement.  Surely if the Size of a subtype,
*whether explicitly specified or not*, allows for efficient independent
addressability, aliased objects and unaliased components should have
the same Size as the subtype.

For example, if T'Size is 32 by default, aliased objects and unaliased
components should have a 'Size of 32 whether or not there's a confirming
size clause "for T'Size use 32".

The only case I can think of where there might be a conflict is for
a subtype that isn't a first subtype, like this:

    subtype Byte_Integer is Integer range -128 .. 127;
    -- Integer'Size = (say) 32, Byte_Integer'Size = 8
    Obj: aliased Byte_Integer;
    -- Obj'Size = 32?

In this case, it's reasonable for Obj'Size to be 32.  This doesn't
conflict with 13.3(50-52), but it does conflict with the interpretation
that it should apply whether or not the size is specified.

I hope I've at least narrowed down the conflicting cases.

****************************************************************

!section 13.1(14)
!subject Size attribute
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5463.a Robert A Duff 96-4-11
!from Bob Duff
!reference 96-5467.a Robert A Duff 96-4-12>>
!discussion

Keith Thompson says:

> In most cases, this [13.3(50-52)] doesn't necessarily violate the
> principle; at worst, it's an incomplete statement.

Technically true.  However, consider the 80386.  You can load 8-bit and
32-bit quantities most efficiently, 16-bit quantities slightly less
efficiently.  3-bit quantities are out of the question, since they would
require extra locking code, but 16-bit quantities are efficient enough
to be called "efficient" -- at least that was *my* intent.

So, if you write:

    type T1 is range 0..2**16-1; -- T1'Size = 16
    X1: T1; -- X1'Size = 32

    type T2 is range 0..2**16-1; -- T2'Size = 16
    for T2'Size use 16;
    X2: T2; -- X2'Size = 16

This is certainly an allowed implementation, and it seems to me a
desirable implementation.  It violates the principle, since T1'Size and
T2'Size are both 16, but the meaning of their Size is different, since
one is "16 by default" and the other is "specified as 16".

Here's another "principle", which I'm not sure anybody else agrees with,
but it makes sense to me: If you don't give any rep clauses, then the
compiler ought to do what's most efficient.  If you care about
representation more than (the compiler's notion of) efficiency, then
give a rep clause.  "Predictable representations" is not a goal, if
there are no rep clauses.

- Bob

****************************************************************

!section 13.1(14)
!subject Size attribute
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5463.a Robert A Duff 96-4-11
!from Bob Duff
!reference 96-5468.a Robert A Duff 96-4-12>>
!discussion

Oops.  In the message I just sent, I forgot to put "aliased".  Sorry.
Here's a correction:

Keith Thompson says:

> In most cases, this [13.3(50-52)] doesn't necessarily violate the
> principle; at worst, it's an incomplete statement.

Technically true.  However, consider the 80386.  You can load 8-bit and
32-bit quantities most efficiently, 16-bit quantities slightly less
efficiently.  3-bit quantities are out of the question, since they would
require extra locking code, but 16-bit quantities are efficient enough
to be called "efficient" -- at least that was *my* intent.

So, if you write:

    type T1 is range 0..2**16-1; -- T1'Size = 16
    X1: aliased T1; -- X1'Size = 32

    type T2 is range 0..2**16-1; -- T2'Size = 16
    for T2'Size use 16;
    X2: aliased T2; -- X2'Size = 16

This is certainly an allowed implementation, and it seems to me a
desirable implementation.  It violates the principle, since T1'Size and
T2'Size are both 16, but the meaning of their Size is different, since
one is "16 by default" and the other is "specified as 16".

Here's another "principle", which I'm not sure anybody else agrees with,
but it makes sense to me: If you don't give any rep clauses, then the
compiler ought to do what's most efficient.  If you care about
representation more than (the compiler's notion of) efficiency, then
give a rep clause.  "Predictable representations" is not a goal, if
there are no rep clauses.

- Bob

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!from Robert Dewar
!reference 96-5486.a Robert A Duff 96-4-13>>
!discussion

(Apparently, Robert has less patience than I do, with the silly headers
required by the mail server.  And I have very little patience indeed --
I had to submit some of my recent comments 3 times.  Anyway, I'll be a
nice guy, and re-submit this for Robert. -- Bob Duff)

Here's Robert's comment:

let me narrow down my size comments to a set of requirements


(which are possibly conflicting)

o  it should be possible to write confirming rep clauses for all
        representation choices made by the compiler

o  the sizes chosen by an Ada 95 compiler should match those of an Ada 83
        compiler

o  (most important). If the second rule is not met, it should be possible
        to write rep clauses that cuase the choice to match that of the
        Ada 83 compiler.

I never liked what was going on with size in RM 95 as you now, but did not
mind too much as long as 'size could be specified for a subtype as was
the case till very late on.

It is taking that away that has left RM 95 completely broken with respect
to the critical third requirement above.

It is merely annoying that the size rules are peculiar, and reflect not
the best approximation of industry pratice in Ada 83, but rathr the
behavior of one particular vendors compiler that was in fact not a
significant presence in the market.

It is much MORE than annoying that there is no easy way to override this
choice.

At the Paris meeting, we considered it essential that a size clause for
a first subtype be imposed on all subsequent subtypes, because otherwise
confirming rep clauses would be impossible.

Ada 95 broke that (why I don't know), but I could live with it because
I could specify size for a subtype.

At this stage I would like to see:

  o  THat compilers be allowed to specify 'size for other than first
     subtypes, but not required to do so.

  o  That we simply add to the rules for statically matching subtypes
     a rule that says that if the sizes are explicitly set and different
     then the subtypes do not statically match.

I don't care too much if you keep the bizarre rule about sizes happening
to be different if the subtype happens to statically match the first subtype,
as long as I can override it.

In fact I don't too much care WHAT is done, since it is only a matter of
aesthetics, not functionality. If the ARG does not solve this question,
e.g. in the manner suggested above, then GNAT will simply implement
'Subtype_Size and solve the problem itself.

This is not a theoretical issue, we are currently not following the RM
rules, because we know they will break customer code in a manner that
is not easy to provide work arounds for (note in particular that there
is no way to easily specify the size of a record component).

P.S. I would in fact be inclined to keep the current RM rules about
minimum size, consider the following:

  type x is (a,b,c);

in Verdix x'size is 8 or somesuch, BUT Verdix allowed pragma Pack to
go ahead and pack to 2 bits anyway. This is inconsistent with the Paris
meeting AI's and with the RM, but in practice, the packing is more
important than the size, so the RM rules are most practical.

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5486.a Robert Dewar
!from Bob Duff
!reference 96-5487.a Robert A Duff 96-4-13>>
!discussion

> let me narrow down my size comments to a set of requirements
>
>
> (which are possibly conflicting)
>
> o  it should be possible to write confirming rep clauses for all
>       representation choices made by the compiler

Good principle, usually, but we know we've violated it in *some* cases.
There are two issues:

    1. There are a few (minor) cases in which specifying a certain aspect
       of representation to a certain value means something different than
       having an implementation choose that same value by default.  This
       is bad, and we've minimized it to just a few cases.

    2. There are many cases in which no rep clause is legal, and therefore
       no confirming rep clause is legal.  For example, rep clauses
       usually require static-ish things, even though dynamic things
       can be queried.

There are really two possible priniciples -- confirming rep clauses,
when legal, should not change semantics, and confirming rep clauses
should always be legal, and should not change semantics.  The second of
these was never even close to true for Ada 83, so perhaps it should be
modified: confirming rep clauses should be legal for the cases that come
up in practise when interfacing, and should not change semantics.

> o  the sizes chosen by an Ada 95 compiler should match those of an Ada 83
>       compiler
>
> o  (most important). If the second rule is not met, it should be possible
>       to write rep clauses that cuase the choice to match that of the
>       Ada 83 compiler.
>
> I never liked what was going on with size in RM 95 as you now, but did not
> mind too much as long as 'size could be specified for a subtype as was
> the case till very late on.
>
> It is taking that away that has left RM 95 completely broken with respect
> to the critical third requirement above.
>
> It is merely annoying that the size rules are peculiar, and reflect not
> the best approximation of industry pratice in Ada 83, but rathr the
> behavior of one particular vendors compiler that was in fact not a
> significant presence in the market.

It was the goal of the Ada 95 rules to nail some things down that were
vague in Ada 83, so that rep clauses could be more portable across (Ada
95) implementations.  In doing that, it may well be that we've broken
compatibility with existing Ada 83 implementations.  We could solve this
problem by going back to the Ada 83 rule, which I paraphrase here: "The
Size attribute means whatever the implementation wants it to, and it's
relationship with Unchecked_Conversion, 'Address, and pragma Pack is up
to the implementation."  I'm being somewhat facetious here -- Robert's
point is that most implementations agreed with each other, and RM95
chooses an interpretation that was only chosen by some obscure Ada 83
implementation.  I'd like to hear some input from compiler vendors here
-- what did your Ada 83 implementations do, and what do think your Ada
95 implementations ought to do?

> It is much MORE than annoying that there is no easy way to override this
> choice.

You can override the choice on a per-object and per-component basis, and
that has a pretty well-defined meaning.  I admit that it's annoying that
you can't do so on a subtype basis.  The MRT removed that feature for
technical reasons.  We could decide to add it back in, I suppose, but
(1) it's a pretty big extension to be adding under the heading
"interpretations", and (2) we need to find a technically correct way to
do it.  If we choose to do so, I think we *can* find a technically
correct way, independent of how the contradiction between 13.1(14) and
13.3(55) is broken.

> At the Paris meeting, we considered it essential that a size clause for
> a first subtype be imposed on all subsequent subtypes, because otherwise
> confirming rep clauses would be impossible.

Unfortunately, Ada 95 allows:

    type T is range 0..1;
    for T'Size use 1;
    subtype S is range -1..1;

Clearly, we cannot require that S'Size be 1.

I suppose we could require it for subtypes whose range happens to be
small enough.

No, that won't work, either, because the range might not be known at
compile time.  I guess the rule would have to say something about
staticness.  Or, we could make it all implementation-defined, and let
implementations worry about how to be compatible with (various) Ada 83
compilers.

> Ada 95 broke that (why I don't know), but I could live with it because
> I could specify size for a subtype.
>
> At this stage I would like to see:
>
>   o  THat compilers be allowed to specify 'size for other than first
>      subtypes, but not required to do so.

Sounds like an extension to me, and not important enough for the ARG to
bless.  But I could live with it.  If we do it, should we not make some
sense of the rules?  Or do you expect implementations to deal with it?
I fear that compilers will simply generate incorrect code for
conversions of access types (see 13.1(14.a-14.i).  I would prefer to
either require it or disallow it, rather than allow it.  If we require
it, of course, we have to make some sense of it.

>   o  That we simply add to the rules for statically matching subtypes
>      a rule that says that if the sizes are explicitly set and different
>      then the subtypes do not statically match.

This can work.  But note that it violates the first principle above,
that confirming rep clauses should work.

> I don't care too much if you keep the bizarre rule about sizes happening
> to be different if the subtype happens to statically match the first subtype,
> as long as I can override it.
>
> In fact I don't too much care WHAT is done, since it is only a matter of
> aesthetics, not functionality. If the ARG does not solve this question,
> e.g. in the manner suggested above, then GNAT will simply implement
> 'Subtype_Size and solve the problem itself.
>
> This is not a theoretical issue, we are currently not following the RM
> rules, because we know they will break customer code in a manner that
> is not easy to provide work arounds for (note in particular that there
> is no way to easily specify the size of a record component).

Unfortunate, indeed.

> P.S. I would in fact be inclined to keep the current RM rules about
> minimum size, consider the following:
>
>   type x is (a,b,c);
>
> in Verdix x'size is 8 or somesuch, BUT Verdix allowed pragma Pack to
> go ahead and pack to 2 bits anyway. This is inconsistent with the Paris
> meeting AI's and with the RM, but in practice, the packing is more
> important than the size, so the RM rules are most practical.

OK, good, we agree on *some* things.  ;-)

Now, don't you think "type X is range 0..2;" is equivalent to the above,
at the machine level, and should work the same way with respect to
'Size?

- Bob

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5486.a Robert Dewar
!reference 96-5487.a Robert A Duff 96-4-13
!from Bob Duff
!reference 96-5494.a Robert A Duff 96-4-16>>
!discussion

Another correction.  Robert Dewar wrote:

> > At the Paris meeting, we considered it essential that a size clause for
> > a first subtype be imposed on all subsequent subtypes, because otherwise
> > confirming rep clauses would be impossible.

And I replied:

> Unfortunately, Ada 95 allows:
>
>     type T is range 0..1;
>     for T'Size use 1;
>     subtype S is range -1..1;

I meant "subtype S is T'Base range -1..1;".  My point was that S has a
                      ^^^^^^
wider range than the first subtype, so:

> Clearly, we cannot require that S'Size be 1.
>
> I suppose we could require it for subtypes whose range happens to be
> small enough.
>
> No, that won't work, either, because the range might not be known at
> compile time.  I guess the rule would have to say something about
> staticness.  Or, we could make it all implementation-defined, and let
> implementations worry about how to be compatible with (various) Ada 83
> compilers.

Sorry for the confusion.

- Bob

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5486.a Robert Dewar
!reference 96-5487.a Robert A Duff 96-4-13
!from Bob Duff
!reference 96-5495.a Robert A Duff 96-4-16>>
!discussion

Robert and I have discussed the Size issue in private e-mail lately.
(It's a hobby we have, which has taken up most of our spare time in the
last few years.  ;-) )

Anyway, Robert recommends the following (I'm paraphrasing what he wrote):

  Keep the RM rules (13.3(55)) as they are with respect to default
  sizes.  Add a rule that the minimum size default rule does not apply
  to subtypes that happen to statically match the first subtype.

  Implementation Permission: *Allow* implementations to support Size
  clauses for secondary subtypes.

  Say that two subtypes do not statically match if their Sizes differ
  (this can be interesting only if one or other of them is a subtype whose
  Size has been set using the above permission).

I can live with Robert's recommendations.  I won't go *quite* so far as
to say I *agree* with them -- I don't strongly disagree.  ;-)  How's that
for wishy-washy?

Robert notes that if the above Implementation Permission is not granted
by the ARG, then GNAT will implement essentially the same thing, but
call it by a different name ('Subtype_Size).  This is important, because
when porting real Ada 83 code to GNAT, such a feature has been needed.

Robert says: P.S. I do not care two hoots about Alignment, I think
having Alignment as a subtype specific attribute is silly, and GNAT
ignores the possibility of different subtypes having different
alignments as much as it can!

Bob says: I agree -- it's not important.  For uniformity, we could
extend the Impl Permission to Alignment, too.  No big deal.  The only
important thing, is that if Alignments of subtypes can be different, we
can't have static matching of those subtypes.  Alignments are largely
implementation dependent anyway, so this is, I suppose, the
implementer's problem.

- Bob

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5486.a Robert Dewar
!reference 96-5487.a Robert A Duff 96-4-13
!reference 96-5495.a Robert A Duff 96-4-16
!from Bob Duff 96-4-17
!reference 96-5498.a Pascal Leroy 96-4-17>>
!discussion

I suppose we'll have an interesting discussion at the next ARG meeting,
assuming RBKD is there.  I, for one, strongly disagree with the suggestions
below, and agree with (the current wording of) AI95-00109

> Anyway, Robert recommends the following (I'm paraphrasing what he wrote):
>
>   Keep the RM rules (13.3(55)) as they are with respect to default
>   sizes.  Add a rule that the minimum size default rule does not apply
>   to subtypes that happen to statically match the first subtype.
>
>   Implementation Permission: *Allow* implementations to support Size
>   clauses for secondary subtypes.

I don't see what is gained by this.  Certainly not portability, since we don't
_require_ support for such clauses.  The only reason for this permission is
that it makes it more likely that, if vendors want to support this capability,
they'll use the same mechanism.  But then we have to assume that vendors are
not completely stupid: if GNAT for instance supports a 'Subtype_Size
attribute, and there appears to be good reasons for other vendors to support
the same capability then it would be sensible for them to use the attribute
'Subtype_Size.  There are many uniformity issues like that, and we don't have
to incorporate them in the language: we can just assume that reasonable
choices will be made.

I suspect that if we put this permission in the language, we'll spend the next
five years explaining how all the rest of the RM works in the presence of a
size clause for subtypes.

>   Say that two subtypes do not statically match if their Sizes differ
>   (this can be interesting only if one or other of them is a subtype whose
>   Size has been set using the above permission).

Now that violates the separation principle (grab your Ada 83 Rationale and
read section 15.1 to refresh your memory) and I think it's BAD, and that the
language is insufficiently broken here to abandon the separation between the
logical properties and the representation.

> Robert notes that if the above Implementation Permission is not granted
> by the ARG, then GNAT will implement essentially the same thing, but
> call it by a different name ('Subtype_Size).  This is important, because
> when porting real Ada 83 code to GNAT, such a feature has been needed.

I would be curious to see an example of such real Ada 83 code that absolutely
requires 'Size clauses for subtype.  So far, no convincing example has been
shown: Robert's Number_Of_Hotels and Number_Of_Companies stuff looked more
like an example of how to misuse subtypes.

_____________________________________________________________________
Pascal Leroy                                    +33.1.30.12.09.68
pleroy@rational.com                             +33.1.30.12.09.66 FAX

****************************************************************

!section 13.3(55)
!subject Size program
!reference RM95-13.3(55)
!reference 96-5497.a Robert A Duff 96-4-17
!from Bob Duff
!reference 96-5499.a Robert A Duff 96-4-19>>
!discussion

Robert Dewar and Pascal Leroy noted some bugs in the program.  Plus I
noticed a couple myself.  Here's another version, with some bugs fixed.

Sorry.  :-(

Send the results directly to me, and once we've gotten things settled,
I'll post to ada-comment.

- Bob

with System; use System;
package Size_Test_Utils is

   procedure Put_Line(S: String);
   -- Print a line with line number.

   procedure Heading(S: String);
   -- Print a heading with blank lines around it.

   type Size_In_Bits is range 0..Max_Int;
   function Img(X: Size_In_Bits) return String;
   -- Shorthand for Size_In_Bits'Image.

   procedure Check(Subtype_Name: String;
                   Subtype_Size: Size_In_Bits;
                   Expected_Size: Size_In_Bits);
   -- Prints out the subtype's Size.  If Subtype_Size /= Expected_Size,
   -- notes that fact.

   procedure Check(Subtype_Name: String;
                   Subtype_Size: Size_In_Bits);
   -- Same as previous, but used when there's no particular expected size.

   -- The following functions are the same as the corresponding
   -- procedures.  They return an irrelevant value.  The purpose
   -- is to be able to call those procedures in a declaration
   -- context.
   function Heading(S: String) return Boolean;
   function Check(Subtype_Name: String;
                  Subtype_Size: Size_In_Bits;
                  Expected_Size: Size_In_Bits) return Boolean;
   function Check(Subtype_Name: String;
                  Subtype_Size: Size_In_Bits) return Boolean;

   type Longest_Signed_Integer is range Min_Int..Max_Int;

end Size_Test_Utils;
with Text_IO; use Text_IO;
package body Size_Test_Utils is

   procedure Put_Line_Number is
   begin
      if Col /= 1 then
         raise Program_Error;
      end if;
      Put(Positive_Count'Image(Line));
      Set_Col(8);
   end Put_Line_Number;

   procedure Put_Line(S: String) is
   begin
      Put_Line_Number;
      Text_Io.Put_Line(S);
   end Put_Line;

   procedure Heading(S: String) is
   begin
      New_Line;
      Set_Col(8);
      Text_Io.Put_Line(S);
      New_Line;
   end Heading;

   function Heading(S: String) return Boolean is
   begin
      Heading(S);
      return True;
   end Heading;

   function Img(X: Size_In_Bits) return String is
   begin
      return Size_In_Bits'Image(X);
   end Img;

   procedure Check(Subtype_Name: String;
                   Subtype_Size: Size_In_Bits;
                   Expected_Size: Size_In_Bits) is
   begin
      Put_Line_Number;
      Put(Subtype_Name & "'Size =" & Img(Subtype_Size));
      if Subtype_Size = Expected_Size then
         Put(" (ok)");
      else
         Put(" (expected" & Img(Expected_Size) & ")");
      end if;
      Text_IO.Put_Line(".");
   end Check;

   procedure Check(Subtype_Name: String;
                   Subtype_Size: Size_In_Bits) is
   begin
      Put_Line_Number;
      Put(Subtype_Name & "'Size =" & Img(Subtype_Size));
      Text_IO.Put_Line(".");
   end Check;

   function Check(Subtype_Name: String;
                  Subtype_Size: Size_In_Bits;
                  Expected_Size: Size_In_Bits) return Boolean is
   begin
      Check(Subtype_Name, Subtype_Size, Expected_Size);
      return True;
   end Check;

   function Check(Subtype_Name: String;
                  Subtype_Size: Size_In_Bits) return Boolean is
   begin
      Check(Subtype_Name, Subtype_Size);
      return True;
   end Check;

end Size_Test_Utils;
with Size_Test_Utils; use Size_Test_Utils;
pragma Elaborate(Size_Test_Utils);
package Size_Test_83_Pkg is

   -- This package prints out the 'Size of various subtypes.
   -- Ada-95-specific code is avoided here.
   -- The "..._Test" variables are ignored -- they're just there so we
   -- can call Check in a declaration context.

   package Signed_Integers is

      H1: Boolean := Heading("Signed Integers:");

      type Positive_0 is range 0..0;
      Positive_0_Test: Boolean :=
        Check("Positive_0", Positive_0'Size, 0);

      Positive_0_Base_Test: Boolean :=
        Check("Positive_0'Base", Positive_0'Base'Size);

      type Positive_1 is range 0..1;
      Positive_1_Test: Boolean :=
        Check("Positive_1", Positive_1'Size, 1);

      Positive_1_Base_Test: Boolean :=
        Check("Positive_1'Base", Positive_1'Base'Size);

      type Positive_2 is range 0..2;
      Positive_2_Test: Boolean :=
        Check("Positive_2", Positive_2'Size, 2);

      Positive_2_Base_Test: Boolean :=
        Check("Positive_2'Base", Positive_2'Base'Size);

      type Integer_3 is range -2..2;
      Integer_3_Test: Boolean :=
        Check("Integer_3", Integer_3'Size, 3);

      Integer_3_Base_Test: Boolean :=
        Check("Integer_3'Base", Integer_3'Base'Size);

      type Billion is range -1_000_000_000 .. 1_000_000_000;

      subtype Sub_Pos_0 is Billion range 0..0;
      Sub_Pos_0_Test: Boolean :=
        Check("Sub_Pos_0", Sub_Pos_0'Size, 0);
      subtype Sub_Pos_1 is Billion range 0..1;
      Sub_Pos_1_Test: Boolean :=
        Check("Sub_Pos_1", Sub_Pos_1'Size, 1);
      subtype Sub_Pos_4 is Billion range 0..15;
      Sub_Pos_4_Test: Boolean :=
        Check("Sub_Pos_4", Sub_Pos_4'Size, 4);
      subtype Sub_Int_7 is Billion range -63..63;
      Sub_Int_7_Test: Boolean :=
        Check("Sub_Int_7", Sub_Int_7'Size, 7);

   end Signed_Integers;
   use Signed_Integers;

   package Enums is

      H1: Boolean := Heading("Enums:");

      type Enum_0 is (Red);
      Enum_0_Test: Boolean :=
        Check("Enum_0", Enum_0'Size, 0);

      Enum_0_Base_Test: Boolean :=
        Check("Enum_0'Base", Enum_0'Base'Size, 0);

      type Enum_1 is (Red, Orange);
      Enum_1_Test: Boolean :=
        Check("Enum_1", Enum_1'Size, 1);

      Enum_1_Base_Test: Boolean :=
        Check("Enum_1'Base", Enum_1'Base'Size, 1);

      type Enum_2 is (Red, Orange, Yellow);
      Enum_2_Test: Boolean :=
        Check("Enum_2", Enum_2'Size, 2);

      Enum_2_Base_Test: Boolean :=
        Check("Enum_2'Base", Enum_2'Base'Size, 2);

      type Enum_4 is ('0', '1', '2', '3', '4', '5', '6', '7', '8',
                      '9', Ten, Eleven, Twelve);
      Enum_4_Test: Boolean :=
        Check("Enum_4", Enum_4'Size, 4);

      Enum_4_Base_Test: Boolean :=
        Check("Enum_4'Base", Enum_4'Base'Size, 4);

      subtype Sub_Enum_0 is Enum_4 range '0'..'0';
      Sub_Enum_0_Test: Boolean :=
        Check("Sub_Enum_0", Sub_Enum_0'Size, 0);
      subtype Sub_Enum_1 is Enum_4 range '0'..'1';
      Sub_Enum_1_Test: Boolean :=
        Check("Sub_Enum_1", Sub_Enum_1'Size, 1);

   end Enums;
   use Enums;

   package Records is

      H1: Boolean := Heading("Records:");

      type Unpacked_Rec is
         record
            A: Positive_2;
            B: Sub_Enum_1;
         end record;
      Unpacked_Rec_Test: Boolean :=
        Check("Unpacked_Rec", Unpacked_Rec'Size);

      type Rec_3 is new Unpacked_Rec;
      pragma Pack(Rec_3);
      Rec_3_Test: Boolean :=
        Check("Rec_3", Rec_3'Size, 3);

      Rec_3_Obj: Rec_3;
      A_Test: Boolean :=
        Check("Rec_3_Obj.A", Rec_3_Obj.A'Size, 2);
      B_Test: Boolean :=
        Check("Rec_3_Obj.B", Rec_3_Obj.B'Size, 1);

      type Rec_10 is
         record
            B1, B2, B3, B4, B5, B6, B7, B8, B9, B10: Boolean;
         end record;
      pragma Pack(Rec_10);
      Rec_10_Test: Boolean :=
        Check("Rec_10", Rec_10'Size, 10);

      Rec_10_Obj: Rec_10;
      B1_Test: Boolean :=
        Check("Rec_10_Obj.B1", Rec_10_Obj.B1'Size, 1);

      type Rec_32 is
         record
            X1, X2, X3: Rec_3;
            X4, X5: Integer_3;
            Two_Bits: Enum_2;
            Ten_Bits: Rec_10;
         end record;
      pragma Pack(Rec_32);
      Rec_32_Test: Boolean :=
        Check("Rec_32", Rec_32'Size, 32);

      Rec_32_Obj: Rec_32;
      X2_Test: Boolean :=
        Check("Rec_32_Obj.X2", Rec_32_Obj.X2'Size, 3);
      X5_Test: Boolean :=
        Check("Rec_32_Obj.X5", Rec_32_Obj.X5'Size, 3);

   end Records;

private

   procedure Require_Body;

end Size_Test_83_Pkg;
with System; use System;

package body Size_Test_83_Pkg is

   package Predefined_Stuff is end;
   package body Predefined_Stuff is
   begin

      Heading("Predefined Stuff:");

      Put_Line("Min_Int = " & Longest_Signed_Integer'Image(Min_Int));
      Put_Line("Max_Int = " & Longest_Signed_Integer'Image(Max_Int));
      Check("Longest_Signed_Integer", Longest_Signed_Integer'Size);

      Put_Line("Storage_Unit = " & Longest_Signed_Integer'Image(Storage_Unit));
      Check("Address", Address'Size);

      Put_Line("Boolean'First = " & Boolean'Image(Boolean'First) & ".");
      Put_Line("Boolean'Last = " & Boolean'Image(Boolean'Last) & ".");
      Check("Boolean", Boolean'Size, 1);

      Put_Line("Integer'First = " & Integer'Image(Integer'First) & ".");
      Put_Line("Integer'Last = " & Integer'Image(Integer'Last) & ".");
      Check("Integer", Integer'Size);

      Put_Line("Natural'First = " & Natural'Image(Natural'First) & ".");
      Put_Line("Natural'Last = " & Natural'Image(Natural'Last) & ".");
      Check("Natural", Natural'Size);

      Put_Line("Positive'First = " & Positive'Image(Positive'First) & ".");
      Put_Line("Positive'Last = " & Positive'Image(Positive'Last) & ".");
      Check("Positive", Positive'Size);

      Put_Line("Character'First = " & Character'Image(Character'First) & ".");
      Put_Line("Character'Last = " & Character'Image(Character'Last) & ".");
      Check("Character", Character'Size, 8);
   end Predefined_Stuff;

   procedure Require_Body is
   begin
      null;
   end Require_Body;

end Size_Test_83_Pkg;
with Size_Test_Utils; use Size_Test_Utils;
with Size_Test_83_Pkg; use Size_Test_83_Pkg;

pragma Elaborate(Size_Test_Utils);
pragma Elaborate(Size_Test_83_Pkg);

package Size_Clause_83_Pkg is

   -- Same as Size_Test_83_Pkg, except this package concentrates on cases
   -- where the Size is specified in an attribute_definition_clause.
   -- Each subtype Foo_Spec corresponds to subtype Foo from
   -- Size_Test_83_Pkg.  The declaration of Foo_Spec and Foo are
   -- the same, except that Foo_Spec has a Size clause.

   use Size_Test_83_Pkg.Signed_Integers;
   use Size_Test_83_Pkg.Enums;
   use Size_Test_83_Pkg.Records;

   package Signed_Integers is

      H1: Boolean := Heading("Signed Integers with Size Clauses:");

      type Positive_0_Spec is range 0..0;
      for Positive_0_Spec'Size use 0;
      Positive_0_Spec_Test: Boolean :=
        Check("Positive_0_Spec", Positive_0_Spec'Size, 0);

      Positive_0_Spec_Base_Test: Boolean :=
        Check("Positive_0_Spec'Base", Positive_0_Spec'Base'Size);

      type Positive_1_Spec is range 0..1;
      for Positive_1_Spec'Size use 1;
      Positive_1_Spec_Test: Boolean :=
        Check("Positive_1_Spec", Positive_1_Spec'Size, 1);

      Positive_1_Spec_Base_Test: Boolean :=
        Check("Positive_1_Spec'Base", Positive_1_Spec'Base'Size);

      type Positive_2_Spec is range 0..2;
      for Positive_2_Spec'Size use 2;
      Positive_2_Spec_Test: Boolean :=
        Check("Positive_2_Spec", Positive_2_Spec'Size, 2);

      Positive_2_Spec_Base_Test: Boolean :=
        Check("Positive_2_Spec'Base", Positive_2_Spec'Base'Size);

      type Integer_3_Spec is range -2..2;
      for Integer_3_Spec'Size use 3;
      Integer_3_Spec_Test: Boolean :=
        Check("Integer_3_Spec", Integer_3_Spec'Size, 3);

      Integer_3_Spec_Base_Test: Boolean :=
        Check("Integer_3_Spec'Base", Integer_3_Spec'Base'Size);

      type Billion_Spec is range -1_000_000_000 .. 1_000_000_000;
      for Billion_Spec'Size use 31;
      Billion_Spec_Test: Boolean :=
        Check("Billion_Spec", Billion_Spec'Size, 31);

      subtype Sub_Pos_0_Spec is Billion_Spec range 0..0;
      Sub_Pos_0_Spec_Test: Boolean :=
        Check("Sub_Pos_0_Spec", Sub_Pos_0_Spec'Size, 0);
      subtype Sub_Pos_1_Spec is Billion_Spec range 0..1;
      Sub_Pos_1_Spec_Test: Boolean :=
        Check("Sub_Pos_1_Spec", Sub_Pos_1_Spec'Size, 1);
      subtype Sub_Pos_4_Spec is Billion_Spec range 0..15;
      Sub_Pos_4_Spec_Test: Boolean :=
        Check("Sub_Pos_4_Spec", Sub_Pos_4_Spec'Size, 4);
      subtype Sub_Int_7_Spec is Billion_Spec range -63..63;
      Sub_Int_7_Spec_Test: Boolean :=
        Check("Sub_Int_7_Spec", Sub_Int_7_Spec'Size, 7);

   end Signed_Integers;
   use Signed_Integers;

   package Enums is

      H1: Boolean := Heading("Enums with Size Clauses:");

      type Enum_0_Spec is (Red);
      for Enum_0_Spec'Size use 0;
      Enum_0_Spec_Test: Boolean :=
        Check("Enum_0_Spec", Enum_0_Spec'Size, 0);

      Enum_0_Spec_Base_Test: Boolean :=
        Check("Enum_0_Spec'Base", Enum_0_Spec'Base'Size, 0);

      type Enum_1_Spec is (Red, Orange);
      for Enum_1_Spec'Size use 1;
      Enum_1_Spec_Test: Boolean :=
        Check("Enum_1_Spec", Enum_1_Spec'Size, 1);

      Enum_1_Spec_Base_Test: Boolean :=
        Check("Enum_1_Spec'Base", Enum_1_Spec'Base'Size, 1);

      type Enum_2_Spec is (Red, Orange, Yellow);
      for Enum_2_Spec'Size use 2;
      Enum_2_Spec_Test: Boolean :=
        Check("Enum_2_Spec", Enum_2_Spec'Size, 2);

      Enum_2_Spec_Base_Test: Boolean :=
        Check("Enum_2_Spec'Base", Enum_2_Spec'Base'Size, 2);

      type Enum_4_Spec is ('0', '1', '2', '3', '4', '5', '6', '7', '8',
                      '9', Ten, Eleven, Twelve);
         for Enum_4_Spec'Size use 4;
      Enum_4_Spec_Test: Boolean :=
        Check("Enum_4_Spec", Enum_4_Spec'Size, 4);

      Enum_4_Spec_Base_Test: Boolean :=
        Check("Enum_4_Spec'Base", Enum_4_Spec'Base'Size, 4);

      subtype Sub_Enum_0_Spec is Enum_4_Spec range '0'..'0';
      Sub_Enum_0_Spec_Test: Boolean :=
        Check("Sub_Enum_0_Spec", Sub_Enum_0_Spec'Size, 0);
      subtype Sub_Enum_1_Spec is Enum_4_Spec range '0'..'1';
      Sub_Enum_1_Spec_Test: Boolean :=
        Check("Sub_Enum_1_Spec", Sub_Enum_1_Spec'Size, 1);

      type Six_Bit_Enum_4_Spec is ('0', '1', '2', '3', '4', '5', '6', '7', '8',
                                '9', Ten, Eleven, Twelve);
         for Six_Bit_Enum_4_Spec'Size use 6; -- Non-default Size
      Six_Bit_Enum_4_Spec_Test: Boolean :=
        Check("Six_Bit_Enum_4_Spec", Six_Bit_Enum_4_Spec'Size, 6);

      Six_Bit_Enum_4_Spec_Base_Test: Boolean :=
        Check("Six_Bit_Enum_4_Spec'Base", Six_Bit_Enum_4_Spec'Base'Size, 6);

      subtype Sub_Six_Bit_Enum_0_Spec is Six_Bit_Enum_4_Spec range '0'..'0';
      Sub_Six_Bit_Enum_0_Spec_Test: Boolean :=
        Check("Sub_Six_Bit_Enum_0_Spec", Sub_Six_Bit_Enum_0_Spec'Size, 0);
      subtype Sub_Six_Bit_Enum_1_Spec is Six_Bit_Enum_4_Spec range '0'..'1';
      Sub_Six_Bit_Enum_1_Spec_Test: Boolean :=
        Check("Sub_Six_Bit_Enum_1_Spec", Sub_Six_Bit_Enum_1_Spec'Size, 1);

      subtype Sub_Six_Bit_Enum_6_Spec is Six_Bit_Enum_4_Spec range '0'..Twelve;
      Sub_Six_Bit_Enum_6_Spec_Test: Boolean :=
        Check("Sub_Six_Bit_Enum_6_Spec", Sub_Six_Bit_Enum_6_Spec'Size, 6);

   end Enums;
   use Enums;

   package Records is

      H1: Boolean := Heading("Records with Size Clauses:");

      type Unpacked_Rec_Spec is
         record
            A: Positive_2_Spec;
            B: Sub_Enum_1_Spec;
         end record;
      Unpacked_Rec_Spec_Test: Boolean :=
        Check("Unpacked_Rec_Spec", Unpacked_Rec'Size);

      type Rec_3_Spec is new Unpacked_Rec_Spec;
      pragma Pack(Rec_3_Spec);
      for Rec_3_Spec'Size use 3;
      Rec_3_Spec_Test: Boolean :=
        Check("Rec_3_Spec", Rec_3_Spec'Size, 3);

      Rec_3_Spec_Obj: Rec_3_Spec;
      A_Test: Boolean :=
        Check("Rec_3_Spec_Obj.A", Rec_3_Spec_Obj.A'Size, 2);
      B_Test: Boolean :=
        Check("Rec_3_Spec_Obj.B", Rec_3_Spec_Obj.B'Size, 1);

      type Rec_10_Spec is
         record
            B1, B2, B3, B4, B5, B6, B7, B8, B9, B10: Boolean;
         end record;
      pragma Pack(Rec_10_Spec);
      for Rec_10_Spec'Size use 10;
      Rec_10_Spec_Test: Boolean :=
        Check("Rec_10_Spec", Rec_10_Spec'Size, 10);

      Rec_10_Spec_Obj: Rec_10_Spec;
      B1_Test: Boolean :=
        Check("Rec_10_Spec_Obj.B1", Rec_10_Spec_Obj.B1'Size, 1);

      type Rec_32_Spec is
         record
            X1, X2, X3: Rec_3_Spec;
            X4, X5: Integer_3_Spec;
            Two_Bits: Enum_2_Spec;
            Ten_Bits: Rec_10_Spec;
         end record;
      pragma Pack(Rec_32_Spec);
      for Rec_32_Spec'Size use 32;
      Rec_32_Spec_Test: Boolean :=
        Check("Rec_32_Spec", Rec_32_Spec'Size, 32);

      Rec_32_Spec_Obj: Rec_32_Spec;
      X2_Test: Boolean :=
        Check("Rec_32_Spec_Obj.X2", Rec_32_Spec_Obj.X2'Size, 3);
      X5_Test: Boolean :=
        Check("Rec_32_Spec_Obj.X5", Rec_32_Spec_Obj.X5'Size, 3);

   end Records;

end Size_Clause_83_Pkg;
with Size_Test_Utils; use Size_Test_Utils;
with Size_Test_83_Pkg; use Size_Test_83_Pkg;
with Size_Clause_83_Pkg; use Size_Clause_83_Pkg;

pragma Elaborate(Size_Test_Utils);
pragma Elaborate(Size_Test_83_Pkg);
pragma Elaborate(Size_Clause_83_Pkg);

package Size_Test_95_Pkg is

   -- Same purpose as Size_Test_83_Pkg and Size_Clause_83_Pkg,
   -- but we put all the Ada-95-only declarations here.

   package Signed_Integers is

      H1: Boolean := Heading("Ada 95 Signed Integers:");

      use Size_Test_83_Pkg.Signed_Integers;
      use Size_Clause_83_Pkg.Signed_Integers;

      subtype Positive_0_B is
        Positive_0'Base range -Positive_0'Last..Positive_0'Last;
      Positive_0_B_Test: Boolean :=
        Check("Positive_0_B", Positive_0_B'Size, 0);

      subtype Positive_0_BB is Positive_0'Base range 0..Positive_0'Last;
      Positive_0_BB_Test: Boolean :=
        Check("Positive_0_BB", Positive_0_BB'Size, 0);

      subtype Positive_0_Spec_B is
        Positive_0_Spec'Base range -Positive_0_Spec'Last..Positive_0_Spec'Last;
      Positive_0_Spec_B_Test: Boolean :=
        Check("Positive_0_Spec_B", Positive_0_Spec_B'Size, 0);

      subtype Positive_0_Spec_BB is
        Positive_0_Spec'Base range 0..Positive_0_Spec'Last;
      Positive_0_Spec_BB_Test: Boolean :=
        Check("Positive_0_Spec_BB", Positive_0_Spec_BB'Size, 0);

      subtype Positive_1_B is
        Positive_1'Base range -Positive_1'Last..Positive_1'Last;
      Positive_1_B_Test: Boolean :=
        Check("Positive_1_B", Positive_1_B'Size, 2);

      subtype Positive_1_BB is Positive_1'Base range 0..Positive_1'Last;
      Positive_1_BB_Test: Boolean :=
        Check("Positive_1_BB", Positive_1_BB'Size, 1);

      subtype Positive_1_Spec_B is
        Positive_1_Spec'Base range -Positive_1_Spec'Last..Positive_1_Spec'Last;
      Positive_1_Spec_B_Test: Boolean :=
        Check("Positive_1_Spec_B", Positive_1_Spec_B'Size, 2);

      subtype Positive_1_Spec_BB is
        Positive_1_Spec'Base range 0..Positive_1_Spec'Last;
      Positive_1_Spec_BB_Test: Boolean :=
        Check("Positive_1_Spec_BB", Positive_1_Spec_BB'Size, 1);

      subtype Positive_2_B is
        Positive_2'Base range -Positive_2'Last..Positive_2'Last;
      Positive_2_B_Test: Boolean :=
        Check("Positive_2_B", Positive_2_B'Size, 3);

      subtype Positive_2_BB is Positive_2'Base range 0..Positive_2'Last;
      Positive_2_BB_Test: Boolean :=
        Check("Positive_2_BB", Positive_2_BB'Size, 2);

      subtype Positive_2_Spec_B is
        Positive_2_Spec'Base range -Positive_2_Spec'Last..Positive_2_Spec'Last;
      Positive_2_Spec_B_Test: Boolean :=
        Check("Positive_2_Spec_B", Positive_2_Spec_B'Size, 3);

      subtype Positive_2_Spec_BB is
        Positive_2_Spec'Base range 0..Positive_2_Spec'Last;
      Positive_2_Spec_BB_Test: Boolean :=
        Check("Positive_2_Spec_BB", Positive_2_Spec_BB'Size, 2);

      subtype Integer_3_B is
        Integer_3'Base range -Integer_3'Last..Integer_3'Last;
      Integer_3_B_Test: Boolean :=
        Check("Integer_3_B", Integer_3_B'Size, 3);

      subtype Integer_3_BB is Integer_3'Base range 0..Integer_3'Last;
      Integer_3_BB_Test: Boolean :=
        Check("Integer_3_BB", Integer_3_BB'Size, 2);

      subtype Integer_3_Spec_B is
        Integer_3'Base range -Integer_3'Last..Integer_3'Last;
      Integer_3_Spec_B_Test: Boolean :=
        Check("Integer_3_Spec_B", Integer_3_Spec_B'Size, 3);

      subtype Integer_3_Spec_BB is Integer_3'Base range 0..Integer_3'Last;
      Integer_3_Spec_BB_Test: Boolean :=
        Check("Integer_3_Spec_BB", Integer_3_Spec_BB'Size, 2);

   end Signed_Integers;

   package Modular_Integers is

      H1: Boolean := Heading("Ada 95 Modular Integers:");

      type Mod_0 is mod 2**0;
      Mod_0_Test: Boolean :=
        Check("Mod_0", Mod_0'Size, 0);

      Mod_0_Base_Test: Boolean :=
        Check("Mod_0'Base", Mod_0'Base'Size);

      type Mod_1 is mod 2**1;
      Mod_1_Test: Boolean :=
        Check("Mod_1", Mod_1'Size, 1);

      Mod_1_Base_Test: Boolean :=
        Check("Mod_1'Base", Mod_1'Base'Size);

      type Mod_2 is mod 2**2;
      Mod_2_Test: Boolean :=
        Check("Mod_2", Mod_2'Size, 2);

      Mod_2_Base_Test: Boolean :=
        Check("Mod_2'Base", Mod_2'Base'Size);

      type Mod_32 is mod 2**32;
      Mod_32_Test: Boolean :=
        Check("Mod_32", Mod_32'Size, 32);

      Mod_32_Base_Test: Boolean :=
        Check("Mod_32'Base", Mod_32'Base'Size);

      type Million is mod 1_000_000;

      subtype Sub_Mod_0 is Million range 0..0;
      Sub_Mod_0_Test: Boolean :=
        Check("Sub_Mod_0", Sub_Mod_0'Size, 0);
      subtype Sub_Mod_1 is Million range 0..1;
      Sub_Mod_1_Test: Boolean :=
        Check("Sub_Mod_1", Sub_Mod_1'Size, 1);
      subtype Sub_Mod_4 is Million range 0..15;
      Sub_Mod_4_Test: Boolean :=
        Check("Sub_Mod_4", Sub_Mod_4'Size, 4);
      subtype Sub_Int_7 is Million range 0..100;
      Sub_Int_7_Test: Boolean :=
        Check("Sub_Int_7", Sub_Int_7'Size, 7);

   end Modular_Integers;

private

   procedure Require_Body;

end Size_Test_95_Pkg;
with System; use System;
with System.Storage_Elements; use System.Storage_Elements;

package body Size_Test_95_Pkg is

   package Predefined_Stuff is end;
   package body Predefined_Stuff is

      H1: Boolean := Heading("Ada 95 Predefined Stuff:");

      type Longest_Binary_Modular is mod Max_Binary_Modulus;
      type Longest_Nonbinary_Modular is mod Max_Nonbinary_Modulus;
      Max_Binary_Modulus_Minus_One: constant := Max_Binary_Modulus - 1;
      Max_Nonbinary_Modulus_Minus_One: constant := Max_Nonbinary_Modulus - 1;
   begin

      Put_Line("Max_Binary_Modulus = " &
        Longest_Binary_Modular'Image(Max_Binary_Modulus_Minus_One) & " + 1");
      Check("Longest_Binary_Modular", Longest_Binary_Modular'Size);

      Put_Line("Max_Nonbinary_Modulus = " &
        Longest_Nonbinary_Modular'Image(Max_Nonbinary_Modulus_Minus_One) &
               " + 1");
      Check("Longest_Nonbinary_Modular", Longest_Nonbinary_Modular'Size);

      Put_Line("Word_Size = " & Longest_Signed_Integer'Image(Word_Size));

      Check("Storage_Element", Storage_Element'Size, Storage_Unit);

      Put_Line("Wide_Character'First = " &
        Wide_Character'Image(Wide_Character'First) & ".");
      Put_Line("Wide_Character'Last = " &
               Wide_Character'Image(Wide_Character'Last) & ".");
      Check("Wide_Character", Wide_Character'Size, 16);
   end Predefined_Stuff;

   procedure Require_Body is
   begin
      null;
   end Require_Body;

end Size_Test_95_Pkg;
-- This is a main program that calls the Ada 83 parts of the Size test.
-- See also Size_Test_95.

-- All the work is done during elaboration of the Size_..._Pkg packages.

with Text_IO; use Text_IO;
with Size_Test_83_Pkg;
with Size_Clause_83_Pkg;
procedure Size_Test_83 is
begin
   New_Line;
   Put_Line("[Size_Test_83 done.]");
end Size_Test_83;
-- This is a main program that calls the Ada 83 *and* Ada 95 parts
-- of the Size test.
-- See also Size_Test_83.

-- All the work is done during elaboration of the Size_..._Pkg packages.

with Text_IO; use Text_IO;
with Size_Test_83_Pkg;
with Size_Clause_83_Pkg;
with Size_Test_95_Pkg;
procedure Size_Test_95 is
begin
   New_Line;
   Put_Line("[Size_Test_95 done.]");
end Size_Test_95;

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5486.a Robert Dewar
!reference 96-5487.a Bob Duff
!from Randy Brukardt (RR Software)
!reference 96-5500.a rbrukardt@BIX.com 96-4-20>>

>It was the goal of the Ada 95 rules to nail some things down that were
>vague in Ada 83, so that rep clauses could be more portable across (Ada
>95) implementations.  In doing that, it may well be that we've broken
>compatibility with existing Ada 83 implementations.  We could solve this
>problem by going back to the Ada 83 rule, which I paraphrase here: "The
>Size attribute means whatever the implementation wants it to, and it's
>relationship with Unchecked_Conversion, 'Address, and pragma Pack is up
>to the implementation."  I'm being somewhat facetious here -- Robert's
>point is that most implementations agreed with each other, and RM95
>chooses an interpretation that was only chosen by some obscure Ada 83
>implementation.  I'd like to hear some input from compiler vendors here
>-- what did your Ada 83 implementations do, and what do think your Ada
>95 implementations ought to do?

Well, I've been intending to get into this discussion since it started last
Fall, but never could figure out where to begin.  Bob has given me an
appropriate starting place.

Ada 83 'Size was very vague.  At the time we were implementing it, there
were no ACVC tests, so we could not tell what the ACVC thought the intent
was.  Nor had the ARG said anything on the matter.  We concluded that 'Size
was determined on a type (not subtype) basis, and that it reflected the
size of a stand-alone object.  (Note that this conclusion is exactly opposite
what Ada 95 chose!)  We made a one exception to this conclusion: 'Size for
a subtype with a radically different representation (such as a constrained
array vs. an unconstrained array) would be different -- and that was largely
a result of ACVC tests expecting that String(1..10)'Size = 10 * Character'Size
(a requirement we found out about only after we had finished implementing
'Size, and one later removed from the tests, I think).

It was clear to use that declarations like the following all had the same
'Size, since 'Size was determined on a type basis:
        Type A Is Range -20000 .. 50000; -- 'Size = 32 = 'Base'Size.
        Subtype B Is Range 0 .. 20000; -- 'Size is still 32.
        Subtype C Is Range 0 .. 200; -- 'Size is still 32.

However, ACVC tests required that components of subtype C (for example) could
be stored in 8 bits.  That led us to two more conclusions: 'Size has no
effect on what rep. clauses can be used, and the components need to be handled
differently than stand-alone objects.  (Up to this point, they both were
handled the same way).

We also thought that 'Size on objects was intended to use discriminant/bounds
values to return the actual size.  That made some complex code, but of course
it was never tested (and probably isn't useful, either).

Eventually, we concluded that 'Size had effect on anything, and was just
another useless Ada 83 construct.

Our final implementation of 'Size looks like:
        1) 'Size can be confirmed on all types.
        2) 'Size can be set on discrete types only.  The 'Size chosen will be
           used to select a base type representation, and in (unsigned) cases,
           a possibly different in memory representation.
           We considered allowing 'Size to be set for some other types.
           In particular, we considered using a specified size to select the
           representation of a pointer (similarly to what the Ada 95 compiler
           for the U2200 does with Convention).  We also considered using
           specified sizes for record types.  The latter was not considered
           important, and the former (long pointers directly supported by
           the compiler) never was implemented.
        3) 'Size is stored in the symbol table to be returned on a user
           query.
        4) 'Size is passed at runtime to generic units (remember we use
           universal sharing as our generic model), mainly so it can be
           returned on a query.  [This was done because of ACVC tests.
           We tried to get the ARG and AVO not require this stupidity in this
           case, but were unsuccessful.]
        5) The compiler stores a stand-alone object size (unfortunately called
           'Length', that is a length of storage units), which is used for
           virutally all important purposes.
        6) 'Size for objects starts with the stand-alone size, then adds
           the size of any indirect components, based on their actual sizes.
           'Size for composite types uses the maximum size of any indirect
           components added to the stand-alone size (when it is not specified;
           note that it cannot be specified for any composite type containing
           indirect components, since such components are not statically
           constrained).
        7) Pack and rep. clauses use a minimum size calculated on the fly for
           a subtype (for discrete types).  This is essentially the same as
           the Ada 95 definition of 'Size.  Composite types always use 'Size =
           stand-alone size, and smaller components are not supported.
        8) Unchecked_Conversion uses the stand-alone object size for size
           matching.  Components are always converted to the stand-alone
           size when read, so that is the most appropriate.  This probably
           is why I always was discussing the difference between register and
           memory-based models for Unchecked_Conversion, which no one else
           ever seemed to understand.

Because of this model, I was always adamantly opposed to 'Size being specifiable
on subtypes.  With the current thinking about these models, I don't think there
is any problem with 'Size specification on discrete subtypes (because 'Size
does not affect stand-alone objects).  But I still don't anything to do
with subtypes having different sizes for composite types.

I do agree with Robert that it should be possible for an Ada 95 compiler to
layout memory the same as the Ada 83 compiler did.  Certainly we want this
to be the case for the vast majority of types in the absence of representation
clauses.  I include Pack in representation clauses; I cannot imagine a situation
where our Ada 95 compiler would get the same results for Pack that it did for
our Ada 83 compiler -- simply because our Ada 83 compiler never implemented
Pack!

I guess I never really understood the root issue here.  "Obviously", a compiler
would store subtypes with the underlying stand-alone object "length"; 'Size
has no effect on the representation of a pointed-at-object.  "Obviously",
most such conversions would work.  (As usual, the conversions themselves are
uninteresting, but 'Access is defined in terms of conversion, so that the
rule does matter.)  The problem, of course, from a language definition point
of view is that we do want to allow compilers to use different representations
for stand-alone objects of different subtypes.  Thus, I suppose we get the
mess.

In any case, I can live with Robert's recommendations.  (See the following
messages for other issues).

BTW, when perusing the ARM, I cannot find any mention of the recommended
level of support for specifying 'Size.  It says a lot about the meaning of
'Size, but not about what (sub)types allow specification.  Have I missed
something, or are we all discussing the wrong issue???

                                Randy Brukardt

****************************************************************

!section 13.2(08)
!subject By-reference types and packing
!reference RM95-13.2(8)
!reference RM95-13.2(9)
!reference RM95-13.3(72)
!reference RM95-13.3(73)
!reference 96-5463.a Bob Duff
!from Randy Brukardt (RR Software)
!reference 96-5501.a rbrukardt@BIX.com 96-4-20>>

The referenced message contains examples of packing.  This brings up a question
in my mind.

    type Device_Info is
        record
            In_Ready: Boolean;
            Out_Ready: Boolean;
        end record;
    pragma Pack(Device_Info); -- Device_Info'Size = 2.

    type Device_Array is Array (1..4) of Device_Info;
    for Device_Array'Component_Size use 2;

    Dev : Device_Array;

    procedure Operate (A : In Out Device_Info);

This example appears to be legal.

Consider the call:
        Operate (Dev(2));

Since the second component of Dev is packed, it will start at bit 2; certainly
it will not start at a storage unit boundary, and will not be directly
addressible.

That means that the item almost certainly will be passed by copy.  Either it
will be directly passed by copy, or it will be passed by reference via a
copy made at the call site.  It can be passed by reference only if all
parameters of that type are passed using a bit pointer of some sort.

Now, consider that a record such as Device_Info can be made into a by-reference
type in at least three ways:
        1) By explicitly declaring it limited.  (This is probably pathological).
        2) By applying pragma Atomic to it.
        3) By applying pragma Volatile to it.
The last two are certainly possible in real programs (which I hopefully
illustrated by my choice of names).

A by-reference type does not allow pass by copy.  Therefore, a bit pointer
must be used to pass the type.  This is quite expensive, and I believe was
not intended.  Worse, a side effect of requiring that in even a single
unlikely case, is that an implementation using universal generic code sharing
will be forced to pass ALL generic private and many generic derived and array
types with bit pointers.

Have I missed a rule somewhere which states that by reference components are
treated similarly to aliased components?  Come to think of it, I can't
find any rule which says that

    type Device_Array is Array (1..8) of Aliased Boolean;
    for Device_Array'Component_Size use 1;

is not required, either.

(Of course, we can avoid the problem by not supporting the system programming
annex, but I doubt that is a good long-term plan...)

                        Randy Brukardt

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5486.a Robert Dewar
!reference 96-5487.a Bob Duff
!reference 96-5500.a rbrukardt@BIX.com 96-4-20
!reference 96-5503.a Robert A Duff 96-4-21>>

> BTW, when perusing the ARM, I cannot find any mention of the recommended
> level of support for specifying 'Size.  It says a lot about the meaning of
> 'Size, but not about what (sub)types allow specification.  Have I missed
> something, or are we all discussing the wrong issue???

For subtypes, look at the last sentence of 13.3(55), "If such a subtype
is a first subtype, then an implementation should support a specified
Size for it that reflects this representation."  13.3(53) is also
relevant.

For objects, there's 13.3(43):

   43  A Size clause should be supported for an object if the specified
       Size is at least as large as its subtype's Size, and corresponds
       to a size in storage elements that is a multiple of the object's
       Alignment (if the Alignment is nonzero).

- Bob

****************************************************************

!section 13.2(08)
!subject By-reference types and packing
!reference RM95-13.2(8)
!reference RM95-13.2(9)
!reference RM95-13.3(72)
!reference RM95-13.3(73)
!reference 96-5463.a Bob Duff
!reference 96-5501.a rbrukardt@BIX.com 96-4-20
!from Bob Duff
!reference 96-5504.a Robert A Duff 96-4-21>>

> Now, consider that a record such as Device_Info can be made into a by-reference
> type in at least three ways:
>       1) By explicitly declaring it limited.  (This is probably pathological).
>       2) By applying pragma Atomic to it.
>       3) By applying pragma Volatile to it.
> The last two are certainly possible in real programs (which I hopefully
> illustrated by my choice of names).

It is certainly true that there is no intention of requiring
pass-by-reference to be implemented using some sort of bit-field
pointers.  Clearly, if any of the above are true, then the
implementation should allocate things on a storage unit boundary,
despite pragma Pack.  In fact, some implementations might require 4-byte
boundaries, or something, which should be allowed.

The same goes for "aliased" things.

- Bob

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5486.a Robert Dewar
!reference 96-5487.a Bob Duff
!reference 96-5500.a rbrukardt@BIX.com 96-4-20
!reference 96-5503.a Robert A Duff 96-4-21
!from Randy Brukardt 96-4-23
!reference 96-5507.a Ian Goldberg  96-4-23>>
!discussion

>> BTW, when perusing the ARM, I cannot find any mention of the =
recommended
>> level of support for specifying 'Size.  It says a lot about the =
meaning of
>> 'Size, but not about what (sub)types allow specification.  Have I =
missed
>> something, or are we all discussing the wrong issue???

>For subtypes, look at the last sentence of 13.3(55), "If such a subtype
>is a first subtype, then an implementation should support a specified
>Size for it that reflects this representation."  13.3(53) is also
>relevant.

Therefore, the only required support for specification of 'Size is for =
discrete and fixed point types, and only a single size is required.  =
That means any specification of 'Size on a composite, access, or float =
type is at the whim of the implementor, as is specification to =
(multiples of) the storage unit size.

Thus, most of the examples we have been discussing are relavant only in =
terms of permitted support, as opposed to required support.

****************************************************************

!section 13.1(14)
!subject Re: Dewar's comments on the SIZE problem and Issue
!reference RM95-13.1(14)
!reference RM95-13.3(55)
!reference 96-5486.a Robert Dewar
!reference 96-5487.a Bob Duff
!reference 96-5500.a rbrukardt@BIX.com 96-4-20
!reference 96-5503.a Robert A Duff 96-4-21
!reference 96-5507.a Ian Goldberg  96-4-23
!from Bob Duff
!reference 96-5526.a Robert A Duff 96-4-29>>
!discussion

> Thus, most of the examples we have been discussing are relavant only in =
> terms of permitted support, as opposed to required support.

Yes.  The question is: should more support be required and/or
recommended?

Note that 13.2(7-9) could, depending on how you read it, be considered
to place further requirements on implementations, in addition to
13.3(55).

- Bob

P.S. Your mail software seems garble things, e.g. it puts strange "="
signs all over, which is kind of annoying.  Especially in the output of
the example program, where it changes "=" to "=3D".

****************************************************************

!section 13.03(42)
!subject Comments to AIs regarding 'Size
!reference AI95-00051
!reference AI95-00109
!from Ken Garlington 96-05-13
!keywords Size
!reference 96-5552.a Ken Garlington  96-5-13>>
!discussion

AI95-00051: It seems to me that the restriction on 'Size for fixed and
floating point values is excessive, and that the justification could
easily apply to any value (e.g., a compiler/architecture could choose a
"special" representation for integers, and stay within the language).

Suggest replacing the current proposal for fixed/floating point with the
following:

  - For fixed point, up to the Size of the largest fixed point
    type supported by the implementation.

  - For floating point, up to the Size of the largest floating point
    type supported by the implementation.

For example, if an architecture had two "special" representations for
floating point (e.g., normal and extended floating point on a
MIL-STD-1750), a sufficiently large Size for a normal floating point
would cause the compiler to generate an extended floating point
representation. If there was only one representation for floating point,
then Size would have no effect. This also seems consistent with the
"minimal size" rule, in that a special representation for a number would
force the allocation of a certain number of bits regardless of Size.

The suggested alternative seems more consistent with the other rules,
while preserving what I think was the intent of the current proposal.

(By the way, I assume that someone has made sure these interpretations
regarding objects have also been carried forward to types where
applicable...)

AI95-00109/01:  I am strongly in favor of the Robert Dewar position
described in the commentary, including his "Ulterior Motive".


****************************************************************

!section 13.3(42)
!subject Comments to AIs regarding 'Size
!reference AI95-00051
!reference AI95-00109
!reference 96-5552.a Ken Garlington  96-5-13
!from Robert Dewar 96-05-18
!keywords Size
!reference 96-5564.a Robert Dewar 96-5-18>>
!discussion

Ken Garlington suggests

  "For example, if an architecture had two "special" representations for
   floating point (e.g., normal and extended floating point on a
   MIL-STD-1750), a sufficiently large Size for a normal floating point
   would cause the compiler to generate an extended floating point
   representation. If there was only one representation for floating point,
   then Size would have no effect. This also seems consistent with the
   "minimal size" rule, in that a special representation for a number would
   force the allocation of a certain number of bits regardless of Size."

This seems very wrong. Remember that Size is a subtype specific attribute
which means that you can get implicit conversions. Consider:

    type x is float digits 5; -- 32 bits by default

    function Sqrt (m : x) return x;

    type y is new x;
    for y'size use 64;

according to Ken's suggestion, y would be represented in double precision,
but when you called Sqrt you would get implicit narrowing to 32 bits. This
seems very undesirable.

The whole point is that Size should not make any significant changes to
the internal representation. The idea of having Size affect the precision
is not at all in the spirit of the RM requirements, and would be actively
undesirable (though not non-conformant, just undesirable). I think it would
be helpful if the AI makes clear that this is undesirable and explains why.

Similarly Size on a fixed-point type can affect the padding, but should
NOT affect the Small value.


****************************************************************

!section 13.3(42)
!subject Comments to AIs regarding 'Size
!reference AI95-00051
!reference AI95-00109
!reference 96-5552.a Ken Garlington  96-5-13
!keywords Size
!reference 96-5564.a Robert Dewar 96-5-18
!from Randy Brukardt
!reference 96-5569.a Ian Goldberg  96-5-22>>
!discussion

(From Randy Brukardt, although the header my say something different.)

>Ken Garlington suggests
>
>  "For example, if an architecture had two "special" representations for
>   floating point (e.g., normal and extended floating point on a
>   MIL-STD-1750), a sufficiently large Size for a normal floating point
>   would cause the compiler to generate an extended floating point
>   representation. If there was only one representation for floating point,
>   then Size would have no effect. This also seems consistent with the
>   "minimal size" rule, in that a special representation for a number would
>   force the allocation of a certain number of bits regardless of Size."
>
>This seems very wrong. Remember that Size is a subtype specific attribute
>which means that you can get implicit conversions. Consider:
>
>    type x is float digits 5; -- 32 bits by default
>
>    function Sqrt (m : x) return x;
>
>    type y is new x;
>    for y'size use 64;
>
>according to Ken's suggestion, y would be represented in double precision,
>but when you called Sqrt you would get implicit narrowing to 32 bits. This
>seems very undesirable.
>
>The whole point is that Size should not make any significant changes to
>the internal representation. The idea of having Size affect the precision
>is not at all in the spirit of the RM requirements, and would be actively
>undesirable (though not non-conformant, just undesirable). I think it would
>be helpful if the AI makes clear that this is undesirable and explains why.

Well, I completely disagree about the 'undesirable' part.

Our (read my) intent for our compiler was always that it would do this.
I don't think we ever did it for floating point types, but we do allow it for
all other types.  (Yes, our compiler [used to] allow explicitly changing
the 'Small value for fixed point types; I think we took it out when some
ACVC test pointed out it was illegal.  I know our compiler has code to
do the needed conversions when parameter passing.)

While changing representations on derived floating types might be
unnecessary, it is very useful to allow this capability on first named
subtypes.  Otherwise, there is no (semi) portable way to select a
particular representation (for interfacing, for example):

        Type My_Flt is Digits 5;
        For My_Flt'Size Use 64;

The only alternative ways is to select more precision than the program
needs, or to add some sort of junk range which may not have anything
to do with the program requirements.

As with any programming, there is a certain amount of user-beware
needed.  If someone specifies something unlikely that loses precision,
that's their problem and just too bad.  (I don't find derived subprograms
on a floating point type particularly likely, and the combination is even
less likely.)

We had to do something similar to be able to convert between the two
representations of pointers on the Unisys 2200.

        Type My_Ada_Ptr Is Access <something>;
        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 <something>;
        For My_Ada_Ptr'Size Use 36;

        Type My_C_Ptr Is New My_Ada_Ptr;
        For My_C_Ptr'Size Use 72;

(I didn't do that because it was a lot more work to implement properly.)

****************************************************************

!section 13.3(42)
!subject Comments to AIs regarding 'Size
!reference AI95-00051
!reference AI95-00109
!reference 96-5552.a Ken Garlington  96-5-13
!keywords Size
!reference 96-5564.a Robert Dewar 96-5-18
!from Robert Dewar
!reference 96-5570.a Robert Dewar 96-5-22>>
!discussion

Randy said, replying to me

>>The whole point is that Size should not make any significant changes to
>>the internal representation. The idea of having Size affect the precision
>>is not at all in the spirit of the RM requirements, and would be actively
>>undesirable (though not non-conformant, just undesirable). I think it would
>>be helpful if the AI makes clear that this is undesirable and explains why.

>Well, I completely disagree about the 'undesirable' part.
>
>Our (read my) intent for our compiler was always that it would do this.
>I don't think we ever did it for floating point types, but we do allow it for
 all other types.  (Yes, our compiler [used to] allow explicitly changing
>the 'Small value for fixed point types; I think we took it out when some
>ACVC test pointed out it was illegal.  I know our compiler has code to
>do the needed conversions when parameter passing.)

As best I understand it, this is a comment from Randy on how he would like
the language to be, but it seems clear that this is not what is intended.
The advice that Size not alter the internal representation of an item is
clearly applicable in this case. Also the strong difference in rules between
Size and Small indicates that the notion that Size could affect Small is
not at all what is intended.

Consider the following

     type x is digits 5;

     procedure Noop (a : in out x) is
     begin
        null;
     end;

     type y is new x;
     for y'size use 64;

It seems quite wrong that a call to Noop with an argument of type y would
modify the value of the argument (by stripping extra precision). This is
precisely why a 'Small attribute clause would not have been allowed for y
in the corresponding fixed-point case, and it seems clear that Size is
only expected to affect padding etc where the conversions do not have
significant change-of-representation semantics. The whole idea of change
of representation in Ada is that it should be explicit, never implicit.

Randy, are you simply saying you don't think the language should have been
designed with this restriction, or are you seriously suggesting that it is
appopriate that 'Size clauses cause a change of representation resulting
in implicit (non-trivial) change of representation conversions.

****************************************************************

!section 13.3(42)
!subject Comments to AIs regarding 'Size
!reference AI95-00051
!reference AI95-00109
!reference 96-5552.a Ken Garlington  96-5-13
!keywords Size
!reference 96-5564.a Robert Dewar 96-5-18
!reference 96-5570.a Robert Dewar 96-5-22
!from Randy Brukardt
!reference 96-5571.a Ian Goldberg  96-5-23>>
!discussion

Robert gives the following example in replying to me (Randy Brukardt):

>Consider the following
>
>     type x is digits 5;
>
>     procedure Noop (a : in out x) is
>     begin
>        null;
>     end;
>
>     type y is new x;
>     for y'size use 64;
>
>It seems quite wrong that a call to Noop with an argument of type y would
>modify the value of the argument (by stripping extra precision). This is
>precisely why a 'Small attribute clause would not have been allowed for y
>in the corresponding fixed-point case, and it seems clear that Size is
>only expected to affect padding etc where the conversions do not have
>significant change-of-representation semantics. The whole idea of change
>of representation in Ada is that it should be explicit, never implicit.

I of course agree that change of representation should be explicit,
never implicit.  But the problem here is (taking some license) that
programmer here is planning on using the EXPLICIT conversion to
or from Y(...) to handle the conversion.  They just forgot that the
Noop routine would be derived.

Normal Ada programmers probably ought to avoid derived routines (for
untagged types) altogether, because the rules are just too complex
for the average programmer to understand.  It took us months to
get this stuff right in our Ada 83 compiler, and I doubt we still have
it right in our Ada 95 compiler.

Anyway, explicit representation change via derived types has a long
history in Ada, including an entire section devoted to the idea.  It seems
very odd to deny a programmer (and implementors) the capability to
specify a representation for floating point and fixed point types, but to
allow it for all other types.  (It is true that the numeric conversion rules
are liberal enough that it is not necessary to do this, which is probably
why it was omitted in the first place.  But it is very natural, given that
it must be done for access, array, and record types.)

In any case, the most useful usage of 'Size to force a representation
is on the first-named subtype of an original type declaration.  That
is a capability which is not present anywhere else in the language,
and one that is needed from time to time.  I think the original
commentor had that case in mind, and that is mainly what I care
about here.  The problem is that it is difficult to separate the
cases.

Some other way to specify a hardware floating-point representation
would also fill the need, of course.  Pragma Float_Rep does not
seem too satisfying, however, since such a usage could not be
portable to another compiler.

Ada 83 generally restricted clauses on derived types to those
without primitive operations.  Such a restriction on representation
changing clauses of any kind could be recommended, and
eliminate Robert's concern, without throwing the baby out with
the bathwater.

(Of course, the fact that 'Size is a "subtype" attribute makes
reasoning about it much more complex.  Once the capability
to specify 'Size for subtypes was removed, there was no
reason to retain "subtype" attributes [other than that they
slightly simplify the description of 'Component_Size and
record component clauses].  Unfortunately, such a change
is too much for the ARG to undertake.)

****************************************************************

!section 13.3(42)
!subject Comments to AIs regarding 'Size
!reference AI95-00051
!reference AI95-00109
!reference 96-5552.a Ken Garlington  96-5-13
!keywords Size
!reference 96-5564.a Robert Dewar 96-5-18
!reference 96-5570.a Robert Dewar 96-5-22
!from Robert Dewar
!reference 96-5572.a Robert Dewar 96-5-23>>
!discussion

Randy said

 (Of course, the fact that 'Size is a "subtype" attribute makes
 reasoning about it much more complex.  Once the capability
 to specify 'Size for subtypes was removed, there was no
 reason to retain "subtype" attributes [other than that they
 slightly simplify the description of 'Component_Size and
 record component clauses].  Unfortunately, such a change
 is too much for the ARG to undertake.)

I don't find the reasoning complex. It is an invention of Ada 83, not
Ada 95 that draws a strong distinction between size and other rep clauses
such as record and enumeration representation clauses. The reason is the
same in Ada 83 and Ada 95, namely that it is assumed that different sizes
do NOT correspond to a significant change in repressenation.

To make Size a type specific attribute in Ada 95 would introduce terrible
upwards incompatibilities (the program I gave with Noop is legal Ada 83,
and legal Ada 95, but would of course be illegal Ada 95 if we followed
Randy's suggestion and made Size a type-related attribute).

The fact of the matter is that Randy is complaining about a design decision
made in Ada 83, which is unchanged in Ada 95. His recommendation of not using
derived types for non-tagged types strikes me as absurd, since this is a much
used feature in both Ada 83 and Ada 95 programs, in particular to effect
change of representation. It sounds like the RR compiler at first missed
the very important distinction between Size and other rep clauses, but this
is a crucial distinction in Ada 83 as well as in Ada 95.

It is quite in order to complain about this design decision, but that does
not translate into action at this stage. I happen to think that the Ada 83
decision (and the corresponding identical Ada 95 semantics) is quite
reasonable, but in any case it is clearly the rule of the language as it
stands, and I do not see any cogent argument for suggesting a change at
this stage.

****************************************************************

!section 13.3(42)
!subject Comments to AIs regarding 'Size
!reference AI95-00051
!reference AI95-00109
!reference 96-5552.a Ken Garlington  96-5-13
!keywords Size
!reference 96-5564.a Robert Dewar 96-5-18
!reference 96-5570.a Robert Dewar 96-5-22
!reference 96-5571.a Ian Goldberg  96-5-23
!from Erhard Ploedereder
!reference 96-5573.a Erhard Ploedereder  96-5-24>>

> Anyway, explicit representation change via derived types has a long
> history in Ada, including an entire section devoted to the idea.  It seems
> very odd to deny a programmer (and implementors) the capability to specify a
> representation for floating point and fixed point types, but to allow it for
> all other types.  (It is true that the numeric conversion rules are liberal
> enough that it is not necessary to do this, which is probably why it was
> omitted in the first place.  But it is very natural, given that it must be
> done for access, array, and record types.)

This argument conveniently ignores that this RM Section (13.6) in Ada9X
talks only about records (and arrays) and requires the absence of inherited
subprograms for derived records with rep.clauses. On might infer from the
omission that such representation change for real types was considered
undesirable.

Note also RM 13.1(10) which imposes the "no-inherited subprograms" rule for
all type-related rep.clauses (anything other than Size and Alignment) on
derived types. It would be most curious if
  for T'Small use ...   were illegal, while
  for T'Size use        were legal with an effect that changes 'Small.

****************************************************************

!section 13.3(40)
!subject Size of Mutable Variables
!reference RM95-13.3(40)
!from Bob Duff
!reference 96-5496.a Robert A Duff 96-4-16>>
!discussion

What is the meaning of the Size attribute for an unconstrained variable
that is of a mutable type?  Consider:

    subtype Small_Natural is Natural range 0..100;
    type Mutable(Len: Small_Natural := 0) is
        record
            Chars: String(1..Len);
        end record;
    X: Mutable;

Suppose the implementation allocates the maximum possible size for X
(perhaps 104 bytes).  The "current size" of the value of X (i.e. the
"constrained size", with Len constrained to 0) is perhaps just 4 bytes
(for Len -- the Chars field is empty).  Should X'Size be the current
size (perhaps 4*8) or the maximum size (perhaps 104*8)?  The RM does not
answer this question.

After some discussion with Tucker and Robert Dewar, I believe the answer
should be the max size.  Here's why: Suppose we add a Size clause to the
above:

    for X'Size use 104*8; -- Specify the Size of the *object*.

Clearly, this will not be legal if 104*8 is less than the max possible
size.  By 13.1(17), a query of X'Size *must* now return 104*8.
13.1(17.a) gives an example of this.  So, in the case of a specified
Size (on the object), the query *cannot* return the current size.

To preserve the principle that confirming rep clauses shouldn't change
things, we must use the same rule even when there's *not* a Size clause.

Now, suppose we have a constrained object:

    subtype Mutable_3 is Mutable(Len => 3);
    Y: Mutable_3;
    Z: constant Mutable := (Len => 3, Chars => "abc");

Here, the compiler will not allocate the max size.  Y'Size and Z'Size
should clearly be the *constrained* Size -- the Size given that Len = 3.

Now, suppose we have a parameter:

    procedure Proc(Param1: Mutable; -- Constant; therefore constrained.
                   Param2: in out Mutable3; -- Constrained.
                   Param3: in out Mutable) is
    begin
        ...
    end Proc;

    Proc(X, X, X); -- Unconstrained actual parameters.
    Proc(Y, Y, Y); -- Constrained actual parameters.

Inside Proc, Param1'Size and Param2'Size should be the constrained size,
based on the value of the actual parameter's discriminant.  The
alternative would be to return the max size if the actual parameter
denotes an unconstrained object.  But that's not really reasonable from
an implementation point of view -- inside Proc, we don't know whether
the actual denotes an unconstrained object.

For Param3, however, we *do* know whether the actual is constrained or
unconstrained -- it is necessary to pass extra dope to implement the
'Constrained attribute.  Therefore, Param3'Size should be the max size,
or the constrained size, depending on the constrainedness of the actual
view.  (Note: I said "view" there because the actual could be another
formal -- we should care about the constrainedness of the actual *view*,
not the constrainedness of the actual *object*.)

Note: all aliased objects are constrained, so the parameter issue does
not come up for access values.  That is, Ptr.all'Size will clearly
always return the constrained size.

My conclusion from all this is that you have to view a declared object
as being a fixed-size container.  Thus, 'Size should return the max
size, if that's what was allocated.

Now, what about implementations that don't allocate the max?  That is,
implementations that use the deallocate/reallocate approach, when
assigning to a mutable record?  Presumably, such an implementation would
return the constrained size, even when the object is unconstrained.  One
might argue that we should make the rules the same for both kinds of
implementation.

However, in the example with the rep clause:

    for X'Size use 104*8; -- Specify the Size of the *object*.

it would seem that the RM forbids, or at least frowns upon, the
deallocate/reallocate implementation in this case.  In any case, the RM
requires X'Size to be 104*8.

Another point is that you can have aliased components of a record.  So
you can have pointers into the middle.  This implies that the
deallocate/reallocate approach is questionable anyway, in the general
case.  (In Ada 83, the same problem occurs when you rename a component.
One Ada 83 compiler, in fact, had exactly this bug -- renaming was
implemented as a pointer to the component, and a size-changing
assignment to the outer record caused the pointer to dangle.  Here, I'm
not talking about a discriminant-dependent component, whose renaming is
illegal -- just a regular component declared before the variant part.
You could get around this for renaming, by implementing renaming
differently -- store a pointer to the beginning of the outermost object,
and an offset.  But that implementation is not reasonable for
access-to-components, because unlike renamings, access values can be
assigned, so you would have a rather severe distributed overhead.  It
seems to me we worried about this case during the 9X design, and we
asked the DR's what to do.  They said to make the rules about
access-to-component the same as those for renaming-of-component, which
essentially forbids the deallocate/reallocate implementation.)

If an implementation does choose the deallocate/reallocate
implementation (presumably just in those cases where it works), then it
should be free to return the constrained size for an unconstrained
object.

It seems to me that the answer to the above questions should, at most,
be Implementation Advice, saying that Size "should" be such-and-such in
these cases.  I don't think a hard requirement is called for in this
case (even for the SP Annex).

- Bob

****************************************************************

!section 13.3(40)
!subject Size of Mutable Variables
!reference RM95-13.3(40)
!reference 96-5496.a Bob Duff 96-4-16
!from Randy Brukardt (RR Software)
!reference 96-5502.a rbrukardt@BIX.com 96-4-20>>
!discussion

Most of the referenced message is confused, to say the least, about the
ways that various implementation models work.

>What is the meaning of the Size attribute for an unconstrained variable
>that is of a mutable type?  Consider:
>
>    subtype Small_Natural is Natural range 0..100;
>    type Mutable(Len: Small_Natural := 0) is
>        record
>            Chars: String(1..Len);
>        end record;
>    X: Mutable;
>
>Suppose the implementation allocates the maximum possible size for X
>(perhaps 104 bytes).  The "current size" of the value of X (i.e. the
>"constrained size", with Len constrained to 0) is perhaps just 4 bytes
>(for Len -- the Chars field is empty).  Should X'Size be the current
>size (perhaps 4*8) or the maximum size (perhaps 104*8)?  The RM does not
>answer this question.

Neither does Ada 83.  We assumed (for some reason which is no longer obvious),
we believed it is the current size.

>After some discussion with Tucker and Robert Dewar, I believe the answer
>should be the max size.  Here's why: Suppose we add a Size clause to the
>above:
>
>    for X'Size use 104*8; -- Specify the Size of the *object*.
>
>Clearly, this will not be legal if 104*8 is less than the max possible
>size.  By 13.1(17), a query of X'Size *must* now return 104*8.
>13.1(17.a) gives an example of this.  So, in the case of a specified
>Size (on the object), the query *cannot* return the current size.

That makes sense.

>To preserve the principle that confirming rep clauses shouldn't change
>things, we must use the same rule even when there's *not* a Size clause.

OK.

>Now, suppose we have a constrained object:
>
>    subtype Mutable_3 is Mutable(Len => 3);
>    Y: Mutable_3;
>    Z: constant Mutable := (Len => 3, Chars => "abc");
>
>Here, the compiler will not allocate the max size.  Y'Size and Z'Size
>should clearly be the *constrained* Size -- the Size given that Len = 3.

Well, that depends on the compiler.  I cannot imagine a situation where our
compiler would allocate record subtypes differently than the base type, even
if we adopted the 'max. size' implementation approach in some cases.

> ....
>
>My conclusion from all this is that you have to view a declared object
>as being a fixed-size container.  Thus, 'Size should return the max
>size, if that's what was allocated.

My first reaction to this statement is that it is first sensible statement
I've seen about 'Size in the entire 15 years I've been working with Ada.
OK, there must have been one other at some point.  :-)

This IS the model that our compiler has ALWAYS used.  For all objects.  For
all components.  For EVERYTHING.

>Now, what about implementations that don't allocate the max?  That is,
>implementations that use the deallocate/reallocate approach, when
>assigning to a mutable record?  Presumably, such an implementation would
>return the constrained size, even when the object is unconstrained.

No, it would return the size of the container.  In Ada 83, it appeared to
me that returning the constrained size was intended.  However, Ada 95 says
(13.3(56)) that the size of the container is returned, not including the
indirectly accessed space.  I would find it very strange that subtypes would
work one way, and objects would work the other.  And it wouldn't make any
sense, either.  For our current implementation, that would be 12*8.

>One might argue that we should make the rules the same for both kinds of
>implementation.

I think that the proposed solution IS the same for both kinds of implementation.

>However, in the example with the rep clause:
>
>    for X'Size use 104*8; -- Specify the Size of the *object*.
>
>it would seem that the RM forbids, or at least frowns upon, the
>deallocate/reallocate implementation in this case.  In any case, the RM
>requires X'Size to be 104*8.

I don't see any forbidding of a deallocate/reallocate implementation here.
I agree that X'Size is 104*8.  We will happly give your your 104 bytes, but
of course we would only use the first 12.  The other 92 would just be ignored.

>Another point is that you can have aliased components of a record.  So
>you can have pointers into the middle.  This implies that the
>deallocate/reallocate approach is questionable anyway, in the general
>case.  (In Ada 83, the same problem occurs when you rename a component.
>One Ada 83 compiler, in fact, had exactly this bug -- renaming was
>implemented as a pointer to the component, and a size-changing
>assignment to the outer record caused the pointer to dangle.  Here, I'm
>not talking about a discriminant-dependent component, whose renaming is
>illegal -- just a regular component declared before the variant part.
>You could get around this for renaming, by implementing renaming
>differently -- store a pointer to the beginning of the outermost object,
>and an offset.  But that implementation is not reasonable for
>access-to-components, because unlike renamings, access values can be
>assigned, so you would have a rather severe distributed overhead.  It
>seems to me we worried about this case during the 9X design, and we
>asked the DR's what to do.  They said to make the rules about
>access-to-component the same as those for renaming-of-component, which
>essentially forbids the deallocate/reallocate implementation.)

I have no clue as to what you are talking about here.  We had this discussion
during the development of Ada 95.  I can assure you that if the rules would
have forbidden the deallocate/reallocate implementation, I would have screamed
bloody murder!  No, the aliased component rules do not cause any problems
for deallocate/reallocate model.  Obviously that compiler you are thinking of
had a severe bug that had nothing whatsoever to do with the implementation
model.

Consider:
Declare
    type Mutable_Again(Len: Small_Natural := 0) is
        record
            Chars: Aliased String(1..Len);
            Counter : Aliased Integer := 0;
        end record;
    X: Mutable_Again;
    X3: Mutable_Again(3);
    Type P_Int Is Access All Integer;
    P : P_Int;
Begin
    P := X.Counter'Access;
    X := (3, "ABC", 10);
    Put (P.All); -- Will print 10.
End;

Our implementation assigns ALL object addresses and component offsets statically.
In this case, for the Pentium machine, we would have:
        X.Len'Offset = 0;
        X.Chars'Offset = 2;
        X.Counter'Offset = 12;
When we assign the pointer, we will store X'Address + 12.
When X is mutated, Chars'Data will be deallocated, and a new one allocated.
That will have no effect on the offset of Counter, so that X'Address + 12
still points at the component Counter, and there is no problem.

Of course, X.Chars'Access would change during this assignment, but it of
course is discriminant dependent, so the 'Access is illegal.
X3.Chars'Access can be taken, but since the type is not mutable, the
item Chars'Data will never be deallocated/reallocated, so again there is no
problem.

>If an implementation does choose the deallocate/reallocate
>implementation (presumably just in those cases where it works), then it
>should be free to return the constrained size for an unconstrained
>object.

As above, it always works.  At least ours does.  Secondly, I don't think we
want it to return the constrained size.  I think we want it to return the
subtype size here, that is Mutable'Size.

>It seems to me that the answer to the above questions should, at most,
>be Implementation Advice, saying that Size "should" be such-and-such in
>these cases.  I don't think a hard requirement is called for in this
>case (even for the SP Annex).

OK by me.  But consider the above first.

>- Bob

        Randy.

****************************************************************

!section 13.3(40)
!subject Size of Mutable Variables
!reference RM95-13.3(40)
!reference 96-5496.a Bob Duff 96-4-16
!reference 96-5502.a rbrukardt@BIX.com 96-4-20
!from Bob Duff
!reference 96-5505.a Robert A Duff 96-4-21>>
!discussion

> Most of the referenced message is confused, to say the least, about the
> ways that various implementation models work.

> >    subtype Mutable_3 is Mutable(Len => 3);
> >    Y: Mutable_3;
> >    Z: constant Mutable := (Len => 3, Chars => "abc");
> >
> >Here, the compiler will not allocate the max size.  Y'Size and Z'Size
> >should clearly be the *constrained* Size -- the Size given that Len = 3.
>
> Well, that depends on the compiler.  I cannot imagine a situation where our
> compiler would allocate record subtypes differently than the base type, even
> if we adopted the 'max. size' implementation approach in some cases.

IMHO such a compiler is pretty badly broken.  It seems essential to me
that you allocate constrained objects using the constrained size.  I
suppose the RM doesn't say that, but neither does it say you can't
allocate 2**32 bits for an integer variable.

> My first reaction to this statement is that it is first sensible statement
> I've seen about 'Size in the entire 15 years I've been working with Ada.

:-)

> I have no clue as to what you are talking about here.

Sorry, I didn't define what I meant by the deallocate/reallocate
approach.  There are actually (at least) two deallocate/reallocate
approaches.  The one *I* was talking about, is where the whole object is
deallocated and then reallocated on assignment.  The compiler I was
talking about had a bug that is *directly* *caused* by this
implementation approach.

You, on the other hand, are talking about deallocating/reallocating just
the discriminant-dependent component.  Yes, I agree that this approach
works fine in the presence of aliased objects and renaming.  (It's a
rather unusual approach, though, is it not?  I was under the impression
that *most* compilers always allocate a single contiguous block of
storage for each object.)

>....We had this discussion during the development of Ada 95.  I can
> assure you that if the rules would have forbidden the
> deallocate/reallocate implementation, I would have screamed bloody
> murder!

Didn't you do just that, numerous times?  ;-) ;-)

Yes, *your* deallocate/reallocate approach is not broken by the Ada 95
rules.  Not to worry.

>...  No, the aliased component rules do not cause any problems
> for deallocate/reallocate model.  Obviously that compiler you are thinking of
> had a severe bug that had nothing whatsoever to do with the implementation
> model.

> As above, it always works.  At least ours does.  Secondly, I don't think we
> want it to return the constrained size.  I think we want it to return the
> subtype size here, that is Mutable'Size.

Actually, I don't think it matters at all what 'Size returns -- if the
object is not a single contiguous region of storage, then the 'Size
attribute seems useless, anyway.

- Bob

****************************************************************

!section 13.3(40)
!subject Size of Mutable Variables
!reference RM95-13.3(40)
!reference 96-5496.a Bob Duff 96-4-16
!reference 96-5502.a rbrukardt@BIX.com 96-4-20
!reference 96-5505.a Robert A Duff 96-4-21
!from Randy Brukardt
!reference 96-5506.a Ian Goldberg  96-4-23>>
!discussion

>> Well, that depends on the compiler.  I cannot imagine a situation =
where our
>> compiler would allocate record subtypes differently than the base =
type, even
>> if we adopted the 'max. size' implementation approach in some cases.

>IMHO such a compiler is pretty badly broken.  It seems essential to me
>that you allocate constrained objects using the constrained size.  I
>suppose the RM doesn't say that, but neither does it say you can't
>allocate 2**32 bits for an integer variable.

I slightly misspoke here.  I was thinking more about layout than size, =
and it is possible to have a constrained record with the same layout as =
the unconstrained parent, but with a different size.

My real concern here is that we don't adopt a set of language rules =
which encourage compilers to lie about what they did.  (This is common =
in Ada 83 compilers for 'Size, I believe)  That does not benefit the =
users of the language
and simply makes more work for the implementors.  My strong desire when =
it comes to 'Size for objects is to simply return the size allocated, =
whatever that was -- and if the size is specified, to allocate that =
size.

>Sorry, I didn't define what I meant by the deallocate/reallocate
>approach.  There are actually (at least) two deallocate/reallocate
>approaches.  The one *I* was talking about, is where the whole object =
is
>deallocated and then reallocated on assignment.  The compiler I was
>talking about had a bug that is *directly* *caused* by this
>implementation approach.

>You, on the other hand, are talking about deallocating/reallocating =
just
>the discriminant-dependent component.  Yes, I agree that this approach
>works fine in the presence of aliased objects and renaming.  (It's a
>rather unusual approach, though, is it not?  I was under the impression
>that *most* compilers always allocate a single contiguous block of
>storage for each object.)

I don't know if it is unusual, but certainly it makes more sense than =
the one you describe, even for Ada 83.  (And, as you say, the approach =
you describe is likely wrong for Ada 95).  Besides being much more =
user-friendly ('Access and 'Address work, mutable subtypes don't need =
their size limited), our
approach does not have any extra cost over the one you describe in the =
most common cases (that is, a single mutable component, when the entire =
type is used as a variable or component).  And it's much easier to write =
the compiler and generate good code when component offsets are always =
static.  The only real downside is with I/O of these types, but that is =
easily handled with attributes similar to the stream attributes (which =
is why I wanted the stream attributes to apply to Sequential_IO and =
Direct_IO -- the current situation is that we end up with 4 sets of =
them.)

****************************************************************

!section 13.3(42)
!subject Size and Alignment of (aliased) objects
!reference AI95-00051/02
!from Erhard Ploedereder 96-07-27
!reference 96-5625.a Erhard Ploedereder  96-7-26>>
!discussion

AI-00051/02 states (among other things):
"For an aliased object, the implementation need not support a Size clause
 that is different from what the implementation would have chosen by
 default."

I do not understand the rationale for "need not", which tends to imply
"tough, but possible". Short of runtime size descriptors with each value
(surely not an implementation model for scalar types), this is a "can not"
and therefore should be a "must not" in language rules. (I, for one, would
not know how to implement the access via an access type, when the size of
the designated scalar subtype is not guaranteed to be equal to the size of the
designated object.)

The sentence above does not cover the case of aliased components with
component_clauses forcing a non-default size.

The AI states further:
"For an aliased object, the implementation need not support an Alignment
clause that is less strict than what the implementation would have
chosen by default."

The same argument as given above applies. This should be a "must not" rule
and the transitive alignment effect of component_clauses within
records with alignment clauses should be added.

Going right along....:
"For an aliased object, if an Address clause or a pragma Import is given,
the implementation need not support an Alignment clause that is stricter
than what the implementation would have chosen by default."

There is no !discussion of why this statement is made. Why is there a
connection between pragma Import or an Address clause and this advice
regarding stricter alignment.  If there is such a rationale, why doesn't the
sentence above extend component-"claused" components of records with address
clauses ?  Many questions, but the AI doesn't answer them.

****************************************************************

!section 13.3(42)
!subject Size and Alignment of (aliased) objects
!reference AI95-00051/02
!reference as: 96-5625.a Erhard Ploedereder  96-7-26>>
!from Robert I. Eachus
!reference 96-5627.a Robert I. Eachus 96-7-29>>
!discussion

 > I do not understand the rationale for "need not", which tends to
 > imply "tough, but possible". Short of runtime size descriptors
 > with each value (surely not an implementation model for scalar
 > types), this is a "can not" and therefore should be a "must not"
 > in language rules. (I, for one, would not know how to implement
 > the access via an access type, when the size of the designated
 > scalar subtype is not guaranteed to be equal to the size of the
 > designated object.)

   If the scope of the type is entirely within a single compilation,
it can be done without descriptors.  So I think tough, but possible in
some circumstances is a legitimate rule, and there is no reason to
forbid something in all cases just because it is impossible in some.

****************************************************************

From: 	Robert Dewar
Sent: 	Monday, March 22, 1999 3:19 PM

<<4. For a non-aliased object, the implementation need not support an
Alignment clause unless the Alignment evenly divides the size (in
storage elements) of the object.
>>

I do not like the terminology of evenly divides. It is much clearer
to say that x is an exact multiple of y.

****************************************************************

From: 	Gary Dismukes
Sent: 	Thursday, April  8, 2004  1:12 PM

[Editor's note: These problems were corrected in version /11 of the AI as
filed.]

In the !summary:

> 2. For a signed integer type, the implementation not support a Size/Alignment

I believe that should be: "... the implementation need not support ..."

>    clause which specifies a value larger than the largest Size/Alignment value
>    that would be chosen by the implementation by default (i.e. in the absence
>    of a representation clause) for any signed integer type. Similarly
>    for modular integer types, fixed point types, enumeration types, record
>    types, and array types.

In the !example section:

>     type T2 is range 1 .. 10;
>     for T2'Size use 2 * Unspecified'Size;
>         -- support might not be recommended
>
>     type T3 is range 1 .. 10;
>     for T3'Size use 2 * Unspecified'Alignment;
>         -- support might not be recommended

It seems odd to say "support might not be recommended" on T2 and T3.

Seems like the phrasing should be simply "-- might not be supported",
since this is a comment on implementations, and the RM wording doesn't
require this support.

****************************************************************

From: 	Stephen W. Baird
Sent: 	Thursday, April  8, 2004  10:35 PM

...
> I believe that should be: "... the implementation need not support ..."

Right.

> It seems odd to say "support might not be recommended" on T2 and T3.
>
> Seems like the phrasing should be simply "-- might not be supported",
> since this is a comment on implementations, and the RM wording doesn't
> require this support.

I intended the comments in this section to refer to the recommended level of
support (which is, after all, the topic of the AI) rather than to
implementations, but I have no objection to changing the comment to "-- might
not be supported" if this seems clearer.

Thanks for the careful reading and the feedback.

****************************************************************



Questions? Ask the ACAA Technical Agent