Version 1.5 of ais/ai-00261.txt

Unformatted version of ais/ai-00261.txt version 1.5
Other versions for file ais/ai-00261.txt

!standard 13.13.2 (31)          01-02-15 AI95-00261/01
!standard 13.13.2 (34)
!class amendment 01-02-13
!status work item 01-02-15
!status received 01-02-13
!priority Low
!difficulty Medium
!subject Extending enumeration types
!summary
!problem
The addition of child libraries facilitates programing by extension. The child, which evolves from the parent, can be either a supplement to or specialization of the parent. In any case the specification of the child should be limited to types and methods specific to the child.
In the case of enumerated types, this approach is violated. Theoretically, the author of the software has to know in advance all of the values of the enumerated type. The child libraries would then specialize this type by employing subtypes.
Moreover, you may not be able to modify the base package (it is a purchased library, it belongs to another department, etc.) In that case, all you can do is declare a new enumeration type with the additional literals and provide conversion functions. But that is error-prone; if additional literals are added to the original type during maintenance, the same change will need to be applied to the new type. But that has to be done explicitly, and it is quite likely to be forgotten. In addition, the conversion routines probably use 'Pos and 'Val, and these will break silently if additional literals are added to the original type.
!proposal
Add enumeration extension types.
Syntax:
enumeration_literal_list ::= (enumeration_literal_specification {, enumeration_literal_specification}) enumeration_type_definition ::= enumeration_literal_list | new parent_enumeration_subtype_mark with enumeration_literal_list
Semantics for enumeration extension types:
The enumeration literals for the type include those corresponding to the parent_subtype_mark and the new enumeration literals. These are all declared at the point of the declaration.
For each enumeration literal of the parent, there is a corresponding literal for the new type (with the corresponding position and ordering). The position number of the first enumeration literal in the extension part is one more than the position number of the last enumeration literal of the parent type. Each literal in the extension part shall be distinct from any literal inherited from the parent type.
If parent_enumeration_subtype_mark denotes a first subtype, then the first subtype of a enumeration extension type is constrained to the base range of the type. Otherwise, the first subtype has the same constraint as the parent_enumeration_subtype_mark.
The parent_enumeration_subtype_mark shall not denote a formal derived enumeration type.
The parent_enumeration_subtype_mark shall not denote a Boolean type (see 3.5.3(1)).
Type conversions between an enumeration extension type and its parent are allowed. For a conversion from the extension to the parent, a check is made that the value belong to the parent type. Constraint_Error is raised if this check fails. For conversions between enumeration types with a common ancestor, conversion is equivalent to converting to the ancestor, then to the target.
Stream attributes for enumeration extension type alway use the default implementation (they are not inherited).
Enumeration representation clauses for an enumeration extension type can either specify all of the codes (as currently), or made specify only the values of the new literals. In that case, the range of the aggregate must be include all new literals, and no inherited literals; the coding of inherited literals is the same as that of the parent type.
Wide_Character is defined as an extension of Character. Thus, type conversions directly allow conversions between the two.
A ramification is that extending an enumeration type with a specified size clause may require specifying a size for the extension if the extension does not fit in the size specified for the parent.
This proposal assumes that AI-263 (Scalar formal types are never static) has been adopted.
!discussion
Inherited primitive operations inherit the subtype according to whatever rule is adopted. Since 3.4(26) says that a conversion to the parent type is done as part of the conversion, there is no problem with such operations (they will raise Constraint_Error if any extension literals are used).
We have a special rule for the constraint of an enumeration extension type, so that the first subtype is not constrained to the original subtype. But the constraint is preserved if the parent is not the first subtype, to work like other derived types.
We considered alternative rules here: no special rule (so users generally would have to use 'Base on extensions -- but that seems silly); always ignoring the constraint of the parent_subtype_mark (but that seems inconsistent with the rest of the language); and extending the range only when Parent'Last is statically equal to Parent'Base'Last.
The last idea seems appealing at first, but has weird effects. For instance, if the parent subtype is not a full range subtype, but happens to have the magic upper bound, it will be extended any way.
Another issue is the difference in behavior between static and dynamic constraints. Consider:
type E is (Red, Blue, Green); function Returns_Green return E is separate; -- always returns Green subtype S1 is E range Red .. Returns_Green; subtype S2 is E range Red .. Green;
type E1 is new S1 with (Mauve); type E2 is new S2 with (Taupe);
E1 (which has a dynamic subtype) would be constrained to Red .. Green, while the static parent E2 would be constrained to Red .. Taupe.
For these reasons, and for simplicity, we adopt the first subtype rule.
Extending from formal derived enumeration type must be disallowed, because we have to preserve the property that there are no duplicate literals (else 'Value would need redefinition); we want static literals; and possibly to avoid contract model problems with extending from Boolean types.
An alternative would be to define an assume-the-best rule to allow such types in the spec (with a recheck on instantiation). (In any case, they have to be prohibited in generic bodies.)
AI-263 needs to be adopted to disallow the following example:
type E1 is (Aa, Bb, Cc); generic type E2 is new E1; package G is Static_Value : constant := E2'Pos (E2'Base'Last); end G; type E3 is new E1 with (Dd, Ee); package I is new G (E2 => E3);
This is most important if Static_Value is used in the body.
The question of extended Boolean types has been answered by prohibiting them. This is the easiest rule, and contract model violations are avoided by we're not allowing extensions from formal types. (Even if an assume-the-best rule is adopted, there is no problem here.)
This rule does meam that a user is on their own if they want a three-valued logic; but that probably is as it ought to be. (Ada cannot anticipate all possible three-valued logics.)
Alternatively, we could define the meaning of all of the Boolean operations (including conditionals and array operations) for an extension. This was judged to be far too complex.
Another alternative would be to define that extensions of Boolean types are not Boolean types. This, however, has contract model problems if a generic has a formal derived type of type Boolean. (Then the operations available would depend on the actual, which is a no-no.) This could be fixed by says that a formal derived Boolean type is not a Boolean type (but that might be incompatible, although it seems very unlikely that any program is using a derived Boolean type) or by defining a new kind of generic formal for enumeration extensions, which is never a Boolean type (in this case, extensions would not match a normal derived type). That seems to be overkill, as formal derived enumeration types are not very useful; thus virtually all uses would be with extensions.
Since the last alternative requires additional baggage, the rule prohibiting extensions from Boolean types was adopted.
!example
Claw contains a package similar to the following:
package Claw.Image_List is type Image_List_Type is tagged private; type Image_Kind_Type is (Bitmap, Icon, Cursor); procedure Load_Image ( Image_List : in out Image_List_Type; Image_Name : in String; Image_Kind : in Image_Kind_Type); ... end Claw.Image_List;
Let's say that you need to create an extended image list object type that can load GIFs and JPEGs as well as bitmaps and icons. You could modify Image_Kind to add these items, but then you would be modifying the base package. If you don't "own" the package in question (as is the case if you are just using Claw), then modifying the source to the package means that every update to Claw means going back in and recreating the modifications. Even if you do "own" the package in question, you have to be careful not to add compatibility issues when you change a spec. We try not to change the spec. of Claw packages without a good reason, because doing so can break user code. In this case, if we add enumeration literals to Image_Kind_Type, Image_Kind_Type'Last will change, and that might break someone's code.
If you had a way of extending Image_Kind_Type, the new package would be easy to create:
with Claw.Image_List; package Claw.Extended_Image_List is type Extended_Image_List_Type is new Claw.Image_List.Image_List_Type with private; type Extended_Image_Kind_Type is new Claw.Image_List.Image_List_Kind with (GIF, JPEG); procedure Load_Image ( Image_List : in out Extended_Image_List_Type; Image_Name : in String; Image_Kind : in Extended_Image_Kind_Type); ... end Claw.Extended_Image_List;
with Claw.Image_List; use Claw.Image_List; package body Claw.Extended_Image_List is procedure Load_Image ( Image_List : in out Extended_Image_List_Type; Image_Name : in String; Image_Kind : in Extended_Image_Kind_Type) is begin case Image_Kind is when GIF => Claw.Image_List.Load_Bitmap (Image_List_Type(Image_List), Convert_GIF_to_Bitmap(Image_Name)); when JPEG => Claw.Image_List.Load_Bitmap (Image_List_Type(Image_List), Convert_JPEG_to_Bitmap(Image_Name)); when others => -- Original kinds. (Would be nice to have a -- way to specify this: -- Extended_Image_Kind_Type'parent'range) Claw.Image_List.Load_Image (Image_List_Type(Image_List), Image_Name, Image_Kind_Type(Image_Kind)); end case; end Load_Image;
end Claw.Extended_Image_List;
(Note that the implementation assumes that these are convertible. I don't think they are useful without that.)
There of course are other ways of doing this, but they get messier when the enumeration is longer. Also consider the advantage for maintenance to this version.
Imagine that Microsoft invents a new kind of image, the Bytemap. We probably would end up adding Bytemap to the Image_Kind_Type enumeration in that case. When the new version of Claw is released, you will start using this new version. With an extensible enumeration type, your extension to support GIF and JPEGs would continue to work unmodified, and it would immediately support Bytemaps. Using one of the workarounds, you extension would either work but fail to support Bytemaps, or it would have to be modified before it would even work.
!ACATS test
!appendix

From: Robert C. Leif, Ph.D.
Sent: Sunday, February 11, 2001 11:13 AM
Topic: Extensible Enumerated Types
Reference: RM95-3.5.1 (pp. 37)
Keywords: Extensible, Enumerated, Types, Supertypes

1. Discussion:
	1.1 Introduction:
The addition of child libraries facilitates programing by extension. The
child, which evolves from the parent, can be either a supplement to or
specialization of the parent. In any case the specification of the child
should be limited to types and methods specific to the child.

In the case of enumerated types, this approach is violated. Theoretically,
the author of the software has to know in advance all of the values of the
enumerated type. The child libraries would then specialize this type by
employing subtypes.

Another argument goes back to minimizing coupling and maximizing cohesion.
For instance, the universe of name prefixes could easily exceed 100. For
instance, in the case of Great Britain, all of the titles of nobility have
to be added. The military and public officials would also have to be
included. A simple example for this is a registration form for SIGAda. Since
one of the best rules for data entry, is to minimize user entry of strings,
pull-down menus with a list of choices are a good solution for the entry of
enumerated types. However, the use of a menu with a list of 100 or more
selections is highly user-hostile. Therefore a subtype will have to be used
to populate the menu. Since it is extremely doubtful that a significant
number of clergy or members of the nobility attend SIGAda, the number of
items in the Name.Prefix menu can be reduced by neither including the
prefixes for the clergy nor the nobility. It would be simpler to have the
values for the clergy and nobility in separate child packages that can if
need be withed. The parent package will have to be modified each time a
prefix necessary for the child package has been discovered to have been
omitted. It should be noted, that it is quite easy to omit, one or two items
in an enumerated type. The flexibility of being able to add elements to an
enumerated type will result in a smaller number of items being included in
the original enumeration and simplify the creation of child packages.

2. Possible Approaches:
type Prefix_Type is (None, Mr, Ms, Miss, Mrs, Dr, Prof, Rev, Other);

(1)
supertype Army_Prefix_Type is (Prefix_Type, Private, Corporal, Sergeant,
Lieutenant, Captain, Major, Colonel, General);

(2)
type Army_Prefix_Type is new Prefix_Type with (Private, Corporal, Sergeant,
Lieutenant, Captain, Major, Colonel, General);

(3)
type Army_Prefix_Type is (Prefix_Type'range & ((Private, Corporal, Sergeant,
Lieutenant, Captain, Major, Colonel, General));

Example (1) is expressive; however it requires a new keyword supertype,
which is an antonym of subtype and thus provides symmetry.
Example (2) uses the "is new" construct, which is severely overloaded.
Example (3) uses the "&" operator and is analogous to string handling.

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

From: Michael Yoder
Sent: Tuesday, February 13, 2001 2:31 PM

I think the case with the most potential difficulty would be

     type E is new Wide_Character with (Fee, Fie, Foe, ...);

which isn't insuperably hard IMO.  I am personally enumerationophilic and
would welcome such an extension, but I suspect the issue has been discussed
previously.  :-)  Speaking as a user, I would rate this feature as nice but
not necessary, though there's been more than one case I can recall where it
would have been welcome.  I am sure I have heard numerous users wish for
it, but can't judge how intense these desires were.

A concomitant extension which would be desirable (especially when extending
Wide_Character) is a way to write a representation clause which specifies
values for only the new literals.  Something like

     for E use Wide_Character'Representation & (Fee => 16#10000#, Fie =>
16#10001#, ...);

Mike Yoder

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

From: Pascal Leroy
Sent: Tuesday, February 13, 2001 4:34 PM

Extensible enumeration types are certainly an interesting idea.  In fact at
some point during the 9X process they were part of the Mapping Document, and
got trimmed.  I think Tuck mentioned them again during the last ARG meeting.

But if you feel that this feature should be added to the language, I
encourage you to write a detailed proposal on how this would be integrated
with the rest of the language, and how it could be implemented.  Coming up
with syntax is very easy (I have no doubt that the "right" syntax is #2,
btw) but that's only about 1% of the work.  I think that the ARG would be
much more interested in looking at this idea if there were a real proposal
on the table, rather than a one-line example of the syntax.

If we had a real proposal, it would also be possible to evaluate the
implementation complexity and balance that with the potential benefits.

If you are interested in writing such a proposal, just send it to
Ada-Comment and it will be given due consideration.

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

From: Robert C. Leif, Ph.D.
Sent: Tuesday, February 13, 2001 8:55 PM

I have taken the liberty of cross-posting my reply to Comp.Lang.Ada, which
is where the previous discussion occurred. You wrote, "I encourage you to
write a detailed proposal on how this would be integrated with the rest of
the language, and how it could be implemented."

I did not see a requirement that in order to propose an extension to Ada one
must be an expert in Ada compilers. In fact in some cases, it may be that
such expertise could even be detrimental. As I have repeatedly stated, there
are both technology driven and market driven aspects to product development.
In my own field of Analytical Cytology (Biomedical Engineering), I would
never limit customer or marketing suggestions to only those that the
individual who made the proposal actually knew how to do the implementation.
In fact one major reason for selecting one of the 3 proposed implementations
is: How easy would it be to teach in a first year computer science course? I
might note that my suggestion came from the problem of implementing a
software package.

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

From: Pascal Leroy
Sent: Wednesday, February 14, 2001 1:36 AM
To: Robert C. Leif, Ph.D.
Cc: Ada-Comment List
Subject: Re: [Ada-Comment] Extensible Enumerated Types

> I did not see a requirement that in order to propose an extension to Ada
> one must be an expert in Ada compilers.

There is certainly no such requirement.  The point I was trying to make is
that, for the ARG to start discussing an amendment to the language, there
has to be a detailed proposal on the table.  I was assuming that you had the
necessary knowledge of Ada to come up with such a proposal.  If not, then
fine, we all have our field of expertise and I for one would be unable to
say anything useful about Analytical Cytology.

But the main point remains: you won't find many people in the ARG who have
enough time and interest to write-up detailed amendments proposals for other
people's good ideas.  So while your comment provides one useful data point,
it is unlikely to be given high priority, unless some expert becomes excited
enough about extensible enumerated types to spend time spelling out the
details.

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

From: dewar@gnat.com
Sent: Wednesday, February 14, 2001 8:14 AM

Well there is a detailed proposal I think, namely the one in the mapping
document. I would suggest that the submitter look at this and see if
it meets the needs, and if so, propose that as the starting point.

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

From: Robert C. Leif, Ph.D.
Sent: Wednesday, February 14, 2001 10:34 AM

I have looked at 3 documents: map-rat-Mar92, map-spec-Mar92, and ms-4.6. I
would greatly appreciate a specific reference.
Thank you.

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

From: Pascal Leroy
Sent: Wednesday, February 14, 2001 9:09 AM

Mike commented:

> Speaking as a user, I would rate this feature as nice but
> not necessary, though there's been more than one case I can recall where
it
> would have been welcome.  I am sure I have heard numerous users wish for
> it, but can't judge how intense these desires were.

I have personally run into a few issues where this capability would have
been useful, but not many.  In a number of instances where my first thought
was, I need extensible enumeration types, it turned out that more careful
analysis of the problem revealed that a data structure more complex than an
enumeration type was needed anyway.

Interestingly enough, this is true of Bob Leif's example.  The same
individual can be a Dr, a Colonel and an Earl, and he would probably use one
title or another (or a combination) depending on the context.  An extensible
enumeration type would force that poor person to choose exactly one title,
which would be unfortunate.

Robert suggested:

> Well there is a detailed proposal I think, namely the one in the mapping
> document. I would suggest that the submitter look at this and see if
> it meets the needs, and if so, propose that as the starting point.

That crossed my mind, but as I recall this was killed quite early in the
game, so I'm not sure if the proposal was very detailed.  Moreover, when
Tuck et al. wrote the mapping document they only had to be compatible with
Ada 83.  Now we also need to be compatible with Ada 95, and that may add
other constraints.

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

From: Tucker Taft
Sent: Wednesday, February 14, 2001 11:33 AM

The Ada 9X proposal was not particularly detailed.  There were two
parts.  One was the type extension part.  That is essentially identical
to the "type Ext_Enum is new Enum with (d,e,f);" suggestion,
and that still seems like the most natural.

The other part was polymorphism, where Enum'Class would require
two words, typically, one being the tag, and the other being
the value.  In retrospect, this capability doesn't seem worth
the trouble, and there would be lots of wording complexity associated
with tagged discrete types, relating to pass-by-reference,
ordering, etc.

Supporting enumeration extension seems easy enough (though the
rep-clause issue is interesting), but it is not clear the benefit
is high enough to justify even the modest effort.

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

From: Randy Brukardt
Sent: Wednesday, February 14, 2001 11:58 AM

I agree. I went and looked in a dusty carton in the far corner of the
basement of our office, and looked at all of the old mapping documents.

The proposal disappeared between Mapping 2.0 (May 1991) and Mapping 3.0
(June 1991). These versions probably were internal use only (my copy of
Mapping 2.0 was faxed me from AETECH), so I doubt Dr. Leif could find it
anywhere.

So, here is the complete proposal (so far as I can find) from Mapping 2.0:

3.5.1 Enumeration Types

An enumeration type may be extended as part of a type derivation by
specifying additional enumeration literals. The enumeration literals for the
type include those corresponding to the parent and the new enumeration
literals. For each enumeration literal of the parent, there is a
corresponding literal for the new type (with the corresponding position and
ordering). The position number of the first enumeration literal in the
extension part is one more than the position number of the last enumeration
literal of the parent type.

Examples:

    type Boolean_with_Unknown is new Boolean with (Unknown);
       -- Boolean_with_Unknown has three enumeration literals,
       -- False, True, and Unknown with position numbers 0, 1, and 2.

    type Rainbow is (Red, Orange, Yellow, Green, Blue, Indigo, Violet);
    type Full_Color is new Rainbow with (Brown, Black, White);
	 -- Full_Color is an enumeration type with the literals:
       -- Red, Orange, Yellow, Green, Blue,
       -- Indigo, Violet, Brown, Black, White

---

That's it. At this point, the mapping still had T'Class for untagged types.
For an untagged type, T'Class has the same operations and implementation as
T, but it also
has implicit conversions to and from types in the class rooted at T. (This
would now be the "covers" rules.)

The section on enumeration representation clauses is empty: apparently, the
interactions of the enumeration extensions with rep. clauses either wasn't
considered, or wasn't considered to be a problem.

Similarly, no changes are indicated in the Operations of Discrete Types
section.

My guess here is that the mapping team didn't see a lot of problems or
interactions with this proposal, but I doubt that every corner of the
language was explored. For instance, I don't see any discussion of the
visibility of the enumeration literals. I.e.

    package Pack1 is
        type Rainbow is (Red, Orange, Yellow, Green, Blue, Indigo, Violet);
    end Pack1;

    with Pack1;
    package Pack2 is
        type Full_Color is new Pack1.Rainbow with (Brown, Black, White);
    end Pack2;

    with Pack2; use Pack2;
    procedure Main is
        Color : Full_Color := Red; -- Is Red visible here?
    begin
        null;
    end Main;

Anyway, enjoy. :-)

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

From: Randy Brukardt
Sent: Wednesday, February 14, 2001 12:58 PM

Tuck said:

> The other part was polymorphism, where Enum'Class would require
> two words, typically, one being the tag, and the other being
> the value.  In retrospect, this capability doesn't seem worth
> the trouble, and there would be lots of wording complexity associated
> with tagged discrete types, relating to pass-by-reference,
> ordering, etc.

I don't see any evidence of this in Mapping 2.0. Perhaps it was in an older
version??

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

From: Tucker Taft
Sent: Thursday, February 15, 2001 10:21 AM

Look in section 3.4.3, paragraphs 2 and 3.  This explains
the (logical) representation of a value of tagged type T'Class.
All such values are discriminated unions over the class
rooted at T, with the tag as the implicit discriminant.
Enumeration types could be tagged, as could any kind of type,
by including the word "tagged" in the ultimate ancestor type
declaration.  Any type derived from a tagged type was itself tagged.

So the polymorphism I was describing only applied if the ultimate
ancestor enumeration was marked "tagged."  So essentially I am
saying we should not bring back elementary tagged types, even if
we bring back elementary type extension.

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

From: Randy Brukardt
Sent: Thursday, February 15, 2001 11:53 AM

Yikes! I never looked at the definition of tagged types. We really had
elementary tagged types at one point?

In any case, I don't think that is a feature that we would need to
resurrect.

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

From: Randy Brukardt
Sent: Wednesday, February 14, 2001 1:30 PM

Tuck said:

> Supporting enumeration extension seems easy enough (though the
> rep-clause issue is interesting), but it is not clear the benefit
> is high enough to justify even the modest effort.

It seems to me that the benefit is mostly political: one less thing that
requires modifying a base class when extending.

This seems like a good (and cheap) thing to add in a full revision, but it
doesn't seem to have the importance to spend time on at this stage of the
effort (where we are trying to standardize additional important features for
implementors to use now).

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

From: Michael Yoder
Sent: Wednesday, February 14, 2001 1:52 PM

I think the polymorphism should be dumped; it makes the feature not worth
the effort.  IMO there are two reasonable paths to adding such a feature,
assuming some shorthand for extension rep specs is devised:

(1) Just make the new type a separate type, and have no new semantics
beyond that.  Conversion between the old and new types must use 'Pos and 'Val.

(2) As in (1), but allow type conversions between two enumeration types if
one's root type is the iterated parent of the other's.  This would disallow
conversion between E1 and E2 if both were extensions of E0, for
example.  Conversion to a wider type would always work, conversion to a
narrower one could propagate Constraint_Error.

Under (1) the "is new" syntax is just a shorthand for existing constructs,
except for the treatment of pseudo-literals like Nul, Del, etc. in
extensions of Character and Wide_Character.  Under (2) the declaration can
be treated as shorthand for describing how name resolution works, but
additional type conversions become legal.

I'd favor going with (1); (2) could be added later since it's an extension
that would make no legal programs illegal.  Also, going with (2) would
require deciding whether to make Wide_Character an extension of Character.  :-)

>Supporting enumeration extension seems easy enough (though the
>rep-clause issue is interesting), but it is not clear the benefit
>is high enough to justify even the modest effort.
I agree.

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

From: Robert C. Leif, Ph.D.
Sent: Wednesday, February 14, 2001 3:37 PM

You wrote:
"This seems like a good (and cheap) thing to add in a full revision, but it
doesn't seem to have the importance to spend time on at this stage of the
effort (where we are trying to standardize additional important features for
implementors to use now)."

Although I sympathize with the problem of priorites, I do believe that
Extensible Enummerated Types should be added to the to do list.


Your second example was:
------------------------------------------
    package Pack1 is
        type Rainbow is (Red, Orange, Yellow, Green, Blue, Indigo, Violet);
    end Pack1;

    with Pack1;
    package Pack2 is
        type Full_Color is new Pack1.Rainbow with (Brown, Black, White);
    end Pack2;

    with Pack2; use Pack2;
    procedure Main is
        Color : Full_Color := Red; -- Is Red visible here?
    begin
        null;
    end Main;
---------------------------------------------------
To my untutored mind, Red is visible.

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

From: dewar@gnat.com
Sent: Wednesday, February 14, 2001 4:55 PM

<<Although I sympathize with the problem of priorites, I do believe that
Extensible Enummerated Types should be added to the to do list.>>

There is really no "to do list" in this sense. As Pascal notes, the first
step is for someone to prepare a detailed proposal. So the next step is
to find someone who is enthusiastic and knowledgable enough to write up
a detailed proposal for consideration.

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

From: Robert A Duff
Sent: Wednesday, February 14, 2001 7:49 PM

> To my untutored mind, Red is visible.

I agree.  That is, the inherited enum lits are implicitly declared at
the place of the derived enum type.

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

From: Ehud Lamm
Sent: Wednesday, February 14, 2001 4:12 PM

>
> It seems to me that the benefit is mostly political: one less thing that
> requires modifying a base class when extending.
>
> This seems like a good (and cheap) thing to add in a full revision,

I don't see a great political advantage. I also don't seem to understand
what is the real issue this feature is supposed to solve. It complicates the
language (though not by much is you kill the polymorphism part), for no
substantial gain. That's funcionality vs. complexity again.

I also think that having tagged type as the only mechanism of extensibilty
is better for orthogonality reasons.

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

From: Randy Brukardt
Sent: Wednesday, February 14, 2001 5:11 PM

The problem is one of creating extensions from packages that are out of your
control.

Let's try an example. Claw contains a package similar to the following:

   package Claw.Image_List is
       type Image_List_Type is tagged private;
       type Image_Kind_Type is (Bitmap, Icon, Cursor);
       procedure Load_Image (
            Image_List : in out Image_List_Type;
            Image_Name : in String;
            Image_Kind : in Image_Kind_Type);
         ...
   end Claw.Image_List;

Let's say that you need to create an extended image list object type that
can load GIFs and JPEGs as well as bitmaps and icons. You could modify
Image_Kind to add these items, but then you would be modifying the base
package. If you don't "own" the package in question (as is the case if you
are just using Claw), then modifying the source to the package means that
every update to Claw means going back in and recreating the modifications.
Even if you do "own" the package in question, you have to be careful not to
add compatibility issues when you change a spec. We try not to change the
spec. of Claw packages without a good reason, because doing so can break
user code. In this case, if we add enumeration literals to Image_Kind_Type,
Image_Kind_Type'Last will change, and that might break someone's code.

If you had a way of extending Image_Kind_Type, the new package would be easy
to create:

   with Claw.Image_List;
   package Claw.Extended_Image_List is
       type Extended_Image_List_Type is new Claw.Image_List.Image_List_Type
            with private;
       type Extended_Image_Kind_Type is new Claw.Image_List.Image_List_Kind
            with (GIF, JPEG);
       procedure Load_Image (
            Image_List : in out Extended_Image_List_Type;
            Image_Name : in String;
            Image_Kind : in Extended_Image_Kind_Type);
         ...
   end Claw.Extended_Image_List;

   with Claw.Image_List; use Claw.Image_List;
   package body Claw.Extended_Image_List is
        procedure Load_Image (
            Image_List : in out Extended_Image_List_Type;
            Image_Name : in String;
            Image_Kind : in Extended_Image_Kind_Type) is
        begin
            case Image_Kind is
                when GIF =>
                     Claw.Image_List.Load_Bitmap (Image_List_Type(Image_List),
                          Convert_GIF_to_Bitmap(Image_Name));
                when JPEG =>
                     Claw.Image_List.Load_Bitmap (Image_List_Type(Image_List),
                          Convert_JPEG_to_Bitmap(Image_Name));
                when others => -- Original kinds. (Would be nice to have a
                               -- way to specify this:
                               -- Extended_Image_Kind_Type'parent'range)
                     Claw.Image_List.Load_Image (Image_List_Type(Image_List),
                          Image_Name, Image_Kind_Type(Image_Kind));
            end case;
        end Load_Image;

   end Claw.Extended_Image_List;

(Note that the implementation assumes that these are convertible. I don't
think they are useful without that.)

There of course are other ways of doing this, but they get messier when the
enumeration is longer. Also consider the advantage for maintenance to this
version.

Imagine that Microsoft invents a new kind of image, the Bytemap. We probably
would end up adding Bytemap to the Image_Kind_Type enumeration in that case.
When the new version of Claw is released, you will start using this new
version. With an extensible enumeration type, your extension to support GIF
and JPEGs would continue to work unmodified, and it would immediately
support Bytemaps. Using one of the workarounds, you extension would either
work but fail to support Bytemaps, or it would have to be modified before it
would even work.

This isn't the sort of showstopping problem that "with type" is intended to
solve (for instance), but it certainly would help. I suspect the reason that
we don't have more cry for such features is that most Ada programmers don't
program by extension in the classic O-O way, and thus don't run into these
problems. Better supporting that style is a political gain for Ada (even if
it isn't a real gain).

Java doesn't have enumerations at all (supposedly) because they couldn't
figure out an appropriate extension mechanism. Clearly, some people think
this is important enough to throw the baby out with the bathwater; the least
we can do is consider it.

> I also think that having tagged type as the only mechanism of extensibility
> is better for orthogonality reasons.

Conceptually, everything should be extensible. The reason for limiting it is
that Ada is a pragmatic language: we want to be able to do things without a
substantial runtime penalty. That seems to be the case here. My main concern
here is one of priorities, not whether this is a good idea (it clearly is).

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

From: Randy Brukardt
Sent: Wednesday, February 14, 2001 5:22 PM

Mike said:

> (1) Just make the new type a separate type, and have no new semantics
> beyond that.  Conversion between the old and new types must
> use 'Pos and 'Val.

You have to support conversions. Ada always supports conversions between
derived types, and it would be weird to change that. Moreover, you have to
have conversions, as the handling of derived subprograms is defined in terms
of conversions. (Of course you have derived subprograms here!) Not to
mention that fact that the conversions are a large part of the benefit of
this feature.

The only way to avoid that would be to say that the new type is *not*
derived. But then you'd really want a different syntax (it ought not look
like a derived type if it isn't one), and *then* changing to be derived
later would not be harmless.

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

From: Tucker Taft
Sent: Thursday, February 15, 2001 10:11 AM

Like Randy, I can't imagine adding this feature without dealing
with conversion.  Half-baked amendments are definitely worse than
no amendment, in my view.

Clearly the new enumeration type is a derived type,
and all untagged types with a common ancestor allow some sort of type
conversion.  I would recommend raising Constraint_Error
on a type conversion if the value is outside the base range of the
nearest common ancestor.  After this check, the normal subtype range check
would be performed.

And yes, I would make Wide_Character an extension of Character.
Using type conversion to convert between the two is extremely natural,
and the base-range checking rule suggested above also seems natural.

For a rep-clause, the simplest rule would be to require named notation
in the enum rep aggregate if only the new literals are to have
their internal codes specified.  When using named notation,
the bounds of the aggregate should probably be either the entire
base range of the new type, or Parent_Type'Last+1 .. New_Type'Last,
nothing half way in between.

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

From: dewar@gnat.com
Sent: Thursday, February 15, 2001 11:23 AM
To: ada-comment@ada-auth.org
Subject: Re: [Ada-Comment] Extensible Enumerated Types

<<And yes, I would make Wide_Character an extension of Character.
Using type conversion to convert between the two is extremely natural,
and the base-range checking rule suggested above also seems natural.>>

is this upwards compatible in all situations (couldn't find a counter
example, but it had me a bit worried).

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

From: Tucker Taft
Sent: Thursday, February 15, 2001 11:56 AM

I don't see how it could be incompatible, since type conversion
involves using a subtype_mark, and overloading isn't permitted
on subtype names.

But of course, I could be wrong.  Incompatibilities are tricky
beasts, as we found out in the Ada 9X process ;-).  A more systematic
analysis would be appropriate as part of writing up the amendment
AI.

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

From: Randy Brukardt
Sent: Thursday, February 15, 2001 1:43 PM

Let me try. :-)

Let's declare a nested function Wide_Character:

package Something is
    function Wide_Character (C : Character) return Standard.Wide_Character
is ...

    WC : Standard.Wide_Character;
end Something;

package body Something is
begin
    WC := Wide_Character (C); -- OK if we have conversions?
end Something;

with Something; use Something;
procedure Main is
   WC : Wide_Character;
begin
   WC := Wide_Character (C); -- Illegal now?
end Main;

Humm...does the visibility or the overloading get applied first? If it's the
visibility, then it there is no problem here. And then I don't see how you
could have a problem.

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

From: Michael Yoder
Sent: Thursday, February 15, 2001 11:51 AM

>Clearly the new enumeration type is a derived type,
>and all untagged types with a common ancestor allow some sort of type
>conversion.  I would recommend raising Constraint_Error
>on a type conversion if the value is outside the base range of the
>nearest common ancestor.  After this check, the normal subtype range check
>would be performed.

I agree about the semantics of type conversion here.


>And yes, I would make Wide_Character an extension of Character.
>Using type conversion to convert between the two is extremely natural,
>and the base-range checking rule suggested above also seems natural.

I agree here also.

>For a rep-clause, the simplest rule would be to require named notation
>in the enum rep aggregate if only the new literals are to have
>their internal codes specified.  When using named notation,
>the bounds of the aggregate should probably be either the entire
>base range of the new type, or Parent_Type'Last+1 .. New_Type'Last,
>nothing half way in between.
>
>-Tuck

Let me try to nail this down.  If I had

     type E0 is (Fee);
     type E1 is new E0 with (Fie, Foe);
     type E2 is new E1 with (Foo);

you would make the first two of these rep specs legal; how about the
last?  Do you mean any parent type above, or only the immediate parent?

     for E2 use (1, 2, 3, 4); -- legal
     for E2 use (Foo => 5); -- legal
     for E2 use (Fie => 2, Foe => 3, Foo => 5); -- legal?

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

From: Randy Brukardt
Sent: Thursday, February 15, 2001 1:36 PM

I think immediate parent would be enough, and it would avoid maintenance
problems (in that adding a literal to the middle type would not break the
rep. clause). But I don't care much.

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

From: Tucker Taft
Sent: Friday, February 16, 2001 3:25 PM

I meant "the" parent.  The more distant relatives are called
the "ancestors".

>      for E2 use (1, 2, 3, 4); -- legal
>      for E2 use (Foo => 5); -- legal
>      for E2 use (Fie => 2, Foe => 3, Foo => 5); -- legal?

No, I didn't mean for that to be legal.

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

From: Christoph Grein
Sent: Thursday, February 15, 2001 12:18 AM

>    package Claw.Image_List is
>        type Image_List_Type is tagged private;
>        type Image_Kind_Type is (Bitmap, Icon, Cursor);
>        procedure Load_Image (
>             Image_List : in out Image_List_Type;
>             Image_Name : in String;
>             Image_Kind : in Image_Kind_Type);
>          ...
>    end Claw.Image_List;
>
>
>    with Claw.Image_List;
>    package Claw.Extended_Image_List is
>        type Extended_Image_List_Type is new Claw.Image_List.Image_List_Type
>             with private;
>        type Extended_Image_Kind_Type is new Claw.Image_List.Image_List_Kind
>             with (GIF, JPEG);
-- Would we also have here both inherited programs
         procedure Load_Image (
              Image_List : in out Extended_Image_List_Type;
              Image_Name : in String;
              Image_Kind : in Image_Kind_Type);
         procedure Load_Image (
              Image_List : in out Image_List_Type;
              Image_Name : in String;
              Image_Kind : in Extended_Image_Kind_Type);
-- or only the first one?
-- The first seems easy, it handles quite naturally (if not overridden)
-- only the when others part of the new operation below.
-- But what should the second one do if not overridden?


>        procedure Load_Image (
>             Image_List : in out Extended_Image_List_Type;
>             Image_Name : in String;
>             Image_Kind : in Extended_Image_Kind_Type);
>          ...
>    end Claw.Extended_Image_List;


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

From: Randy Brukardt
Sent: Thursday, February 15, 2001 11:43 AM

Chistoph said:

> -- Would we also have here both inherited programs
>          procedure Load_Image (
>               Image_List : in out Extended_Image_List_Type;
>               Image_Name : in String;
>               Image_Kind : in Image_Kind_Type);
>          procedure Load_Image (
>               Image_List : in out Image_List_Type;
>               Image_Name : in String;
>               Image_Kind : in Extended_Image_Kind_Type);
> -- or only the first one?
> -- The first seems easy, it handles quite naturally (if not overridden)
> -- only the when others part of the new operation below.
> -- But what should the second one do if not overridden?

Humm, I hadn't thought about the second one.

But it isn't a problem; it's not primitive for Image_List_Type, and it will
work OK.

As with all derived types, the call is effectively:
     Load_Image (Image_List, Image_Name, Image_Kind_Type(Image_Kind);

Remember that conversions to the original enumeration type from an extended
one raise Constraint_Error if given a literal outside of the original type.
So
     Load_Image (IL, "My_Name", Extended_Enumeration_Type'(Bitmap));
would work fine, and
     Load_Inage (IL, "My_Name", GIF);
would raise Constraint_Error.

A more interesting problem with this example is that a call to Load_Image
would be ambiguous for the original enumeration literals (if there are use
clauses on both packages):

    Load_Image (IL, "My_Name", Bitmap); -- Ambiguous.

while

    Load_Image (IL, "My_Name", Claw.Extended_Image_List.Bitmap); -- OK

and

    Load_Image (IL, "My_Name", Extended_Image_Kind_Type'(Bitmap)); -- OK

Another reason to avoid promiscuous use clauses.

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

From: dismukes@gnat.com
Sent: Thursday, February 15, 2001 2:28 PM

> package body Something is
> begin
>     WC := Wide_Character (C); -- OK if we have conversions?

The above is legal in any case because the function Wide_Character
hides the type name (independent of whether conversions are allowed).

> end Something;
>
> with Something; use Something;
> procedure Main is
>    WC : Wide_Character;
> begin
>    WC := Wide_Character (C); -- Illegal now?
> end Main;
>
> Humm...does the visibility or the overloading get applied first? If it's the
> visibility, then it there is no problem here. And then I don't see how you
> could have a problem.

Well, you can't do overload resolution until you know what's visible.
The type name hides the use-visible function, so the above has to
be a conversion to the type.  So no problem here.

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

From: Tucker Taft
Sent: Friday, February 16, 2001 3:33 PM

> Let's declare a nested function Wide_Character:
>
> package Something is
>     function Wide_Character (C : Character) return Standard.Wide_Character
> is ...

You have a body in your spec?  Perhaps this is an instantiation of
something ...
>
>     WC : Standard.Wide_Character;
> end Something;
>
> package body Something is
> begin
>     WC := Wide_Character (C); -- OK if we have conversions?

The local declaration of Wide_Character hides the Standard
declaration, so there is no ambiguity.

> end Something;
>
> with Something; use Something;
> procedure Main is
>    WC : Wide_Character;
> begin
>    WC := Wide_Character (C); -- Illegal now?

The declaration in package Standard hides the one from the
"use" clause, so this is illegal in Ada 95, but would be legal
if Wide_Character were an extension of Character.  This
is not an incompatibility, this is a pure language extension.

> end Main;
>
> Humm...does the visibility or the overloading get applied first? If it's the
> visibility, then it there is no problem here. And then I don't see how you
> could have a problem.

Type declarations are non-overloadable, so they never get mixed
up with function declarations (or array declarations, or ...),
so there really can't be any incompatibilities.  Any existing
code that would resolve the name "Wide_Character" so that it
referred to Standard.Wide_Character could not change in meaning
due to this change.  Some illegal code could become legal, but
that's it.

Usual caveats apply -- I might be wrong ;-).

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

From: Robert A Duff
Sent: Thursday, February 15, 2001 6:31 PM

> > I don't see any evidence of this in Mapping 2.0. Perhaps it was in an older
> > version??
>
> Look in section 3.4.3, paragraphs 2 and 3.

Are these mappings available online somewhere?  (They should be, for
historical interest if nothing else.)  I have stacks of paper Ada 9X
documents in cardboard boxes in my attic, but I'm not sure how complete
they are, and anyway I'd prefer to search some on-line documents.

I joined the project in 1991, I think, but there was a lot of
interesting work before that.

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

From: Randy Brukardt
Sent: Thursday, February 15, 2001 6:45 PM

I doubt it. The Mapping 2.0 version was a draft version distributed only to
the DRs and the U/I teams. Thus, I doubt versions were made for posting at
Ada IC: especially since the conversion tools in those days were primitive.
There might be on-line versions of the public documents in the deep, dark
corners of the Ada IC web site, and that could be interesting, but there is
a lot of other work that probably is not on line at all.

I found the text I noted in a dusty box, after all, and laboriously keyed it
in by hand.

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

From: Baird, Steve
Sent: Thursday, February 15, 2001 5:45 PM

The following is a list of some of the language issues that would
have to be addressed if enumeration type extensions were to be
added to the language (an action which I do not endorse).

No one of these would be particularly difficult to deal with,
but this is just a sampling and undoubtedly there would be others.

The point I am trying to make is that this would not be a "simple"
change.

  1) How would stream-oriented attributes behave, both in the case
     where the corresponding attribute of the parent type has the
     default implementation and in the case where it has a
     user-defined implementation?

  2) It seems like the parent subtype must be unconstrained.
     What would this mean
          type E1 is (Aa, Bb, Cc);
          subtype S is E1 range Aa .. Bb;
          type E2 is new S with (Dd, Ee);
     ?

  3) Perhaps it falls out, but clarification might be needed
     to ensure that a case like

         type E1 is (Aa, Bb, Cc);
         type Aa_Again is new E1 with (Dd, Aa);

     is rejected (i.e. the explicit-takes-precedence-over-implicit
     rule would not apply here).

     For similar reasons, enumeration extension of a formal derived
     (enumeration) type should be disallowed.

  4) 4.6(28) "...this can only happen on conversion to a modular
     type" would require modification. It might be worth stating
     explicitly that extension values of two structurally similar
     extensions are not corresponding values. For example,
         type E1 is (Aa, Bb, Cc);
         type E2 is new E1 with (Dd);
         type E3 is new E1 with (Dd);
         X3 : E3 := E3 (E2'(Dd));
     the elaboration of X3 would raise Constraint_Error.

  5) 3.4(9) and 13.1(5) would obviously require modification.
     Consider, for example, the attributes LAST and SIZE.

  6) This example should clearly be rejected,

         type E1 is (Aa, Bb, Cc);
         generic
             type E2 is new E1;
         package G is
             Static_Value : constant := E2'Pos (E2'Base'Last);
         end G;
         type E3 is new E1 with (Dd, Ee);
         package I is new G (E2 => E3);

     but on what basis? Does the definition of "static scalar
     subtype" need to be changed (i.e. treat a formal derived
     scalar type the same as a formal scalar type in 4.9(26)), or
     should the matching rules of 12.5.1(7-10) be tightened up?

  7) Would 3.4(19) require modification? Is it already clear
     (with no wording changes) that in this example
         package Pkg1 is
             type E1 is (Aa, Bb, Cc);
             procedure Proc (X : E1);
         end Pkg;
         package Pkg2 is
             type E2 is new Pkg1.E1 with (Dd, Ee);
         end;
     the subtype of Pkg2.Proc.X would have 3 values, not 5?

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

From: Randy Brukardt
Sent: Thursday, February 15, 2001 7:49 PM

> The following is a list of some of the language issues that would
> have to be addressed if enumeration type extensions were to be
> added to the language (an action which I do not endorse).
>
> No one of these would be particularly difficult to deal with,
> but this is just a sampling and undoubtedly there would be others.
>
> The point I am trying to make is that this would not be a "simple"
> change.

Of course this is a simple change. If there are only 7 issues, it's real
simple. :-) From experience, there will be dozens of issues for any
proposal, no matter how simple, that was made to the ARG.

I don't find this proposal particularly important, but finding a few minor
issues to work out certainly shouldn't disqualify it from consideration.

In any event, I was directed to create an AI for this, and I will make it as
useful as possible. To that end, I'm creating a summary of the proposal in
the AI (no wording, though). *Then* the ARG can reject it.

>   1) How would stream-oriented attributes behave, both in the case
>      where the corresponding attribute of the parent type has the
>      default implementation and in the case where it has a
>      user-defined implementation?

There can't possibly be any question about the default behavior: it would be
the same as any other elementary type. Why would it be different? The
user-defined case is a bit more interesting. But there is no requirement
(with the Corrigendum wording) that an attribute is inherited in any way, so
I would suggest that it should simply always use the default implementation.
I'll add that to the AI.

>   2) It seems like the parent subtype must be unconstrained.
>      What would this mean
>           type E1 is (Aa, Bb, Cc);
>           subtype S is E1 range Aa .. Bb;
>           type E2 is new S with (Dd, Ee);
>      ?

No, there are no unconstrained enumeration subtypes. The point is that you
are deriving the *type*, not the *subtype*. The wording would need to be
clear on this. So, the effect is the same if you derive from S or from E1.

>   3) Perhaps it falls out, but clarification might be needed
>      to ensure that a case like
>
>          type E1 is (Aa, Bb, Cc);
>          type Aa_Again is new E1 with (Dd, Aa);
>
>      is rejected (i.e. the explicit-takes-precedence-over-implicit
>      rule would not apply here).

I think it falls out from the rule that the parent literals are redeclared.
But wording care would be necessary.

>      For similar reasons, enumeration extension of a formal derived
>      (enumeration) type should be disallowed.

Makes sense. I don't think an assume-the-best/assume-the-worst rule would be
justified here.

>   4) 4.6(28) "...this can only happen on conversion to a modular
>      type" would require modification. It might be worth stating
>      explicitly that extension values of two structurally similar
>      extensions are not corresponding values. For example,
>          type E1 is (Aa, Bb, Cc);
>          type E2 is new E1 with (Dd);
>          type E3 is new E1 with (Dd);
>          X3 : E3 := E3 (E2'(Dd));
>      the elaboration of X3 would raise Constraint_Error.

4.6(28): of course. That's obviously part of updating the wording for
conversions.

The tricky part of defining conversions is handling the case where there are
two
extensions with a common ancestor. The only rule that makes sense to me is
"For conversions between enumeration types with a common ancestor,
conversion is equivalent to converting to the ancestor, then to the target."
With that, there is no need to discuss "structurally similar extensions",
except perhaps in the AARM.

>   5) 3.4(9) and 13.1(5) would obviously require modification.
>      Consider, for example, the attributes LAST and SIZE.

3.4(9): of course. 13.1(5): I think you meant 13.1(15). I don't think there
is a problem with Size (which is subtype-specific): it only is inherited of
the subtype is statically matching, which it clearly is not. Last is neither
an operational nor representation attribute, so it doesn't fall under
13.1(15) anyway.

>   6) This example should clearly be rejected,
>
>          type E1 is (Aa, Bb, Cc);
>          generic
>              type E2 is new E1;
>          package G is
>              Static_Value : constant := E2'Pos (E2'Base'Last);
>          end G;
>          type E3 is new E1 with (Dd, Ee);
>          package I is new G (E2 => E3);
>
>      but on what basis? Does the definition of "static scalar
>      subtype" need to be changed (i.e. treat a formal derived
>      scalar type the same as a formal scalar type in 4.9(26)), or
>      should the matching rules of 12.5.1(7-10) be tightened up?

I don't get it. What's the problem here? I.Static_Value is clearly 5 in this
case.

Actually, 4.9(26) says that there aren't any static enumeration types, so it
needs to be fixed in any case.

That follows because there are no unconstrained enumeration subtypes. The
interesting part of the paragraph says:

A static scalar subtype is an unconstrained scalar subtype whose type is not
a descendant of a formal scalar type, or a constrained scalar subtype formed
by imposing a compatible static constraint on a static scalar subtype.

Since there are no unconstrained enumeration subtypes, the first part is
always false for an enumeration type. The second part requires already
having a static scalar subtype (which we don't have because the first part
is false). So there are no static enumeration subtypes. QED. :-)

>   7) Would 3.4(19) require modification? Is it already clear
>      (with no wording changes) that in this example
>          package Pkg1 is
>              type E1 is (Aa, Bb, Cc);
>              procedure Proc (X : E1);
>          end Pkg;
>          package Pkg2 is
>              type E2 is new Pkg1.E1 with (Dd, Ee);
>          end;
>      the subtype of Pkg2.Proc.X would have 3 values, not 5?

There are no unconstrained enumeration subtypes now. So enumeration
extension types would not change that, and certainly 3.4(19) would not need
to be changed. (The constraint would be inherited).


Thanks for improving the proposal (even if that wasn't your intent).

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

From: Robert A Duff
Sent: Thursday, February 15, 2001 8:01 PM

> The point I am trying to make is that this would not be a "simple"
> change.

Thank you, Steve, for your careful analysis!
I agree that this would not be a "simple" change.

>   5) 3.4(9) and 13.1(5) would obviously require modification.
>      Consider, for example, the attributes LAST and SIZE.

I don't understand the issue with 13.1(5).  I see your point about all
the other stuff.  (You should print out Randy's fancy new version, which
has version 13.1(5/1), but I don't think it makes any difference in this
case.)

I have wanted extensible enumerated types several times, but it's not at
the top of my priority list.  Let's worry about "with private" first.
And extensible enumerated types later, or never.

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

From: dewar@gnat.com
Sent: Thursday, February 15, 2001 11:34 PM

<<There can't possibly be any question about the default behavior: it would be
the same as any other elementary type. Why would it be different? The
user-defined case is a bit more interesting. But there is no requirement
(with the Corrigendum wording) that an attribute is inherited in any way, so
I would suggest that it should simply always use the default implementation.
I'll add that to the AI.>>

One interesting point is that the represntation of the extended type may
be quite different from the base type (e.g. wide character is 16 bits,
and character is 8 bits). Would a size clause constrain the derived type?

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

From: Pascal Leroy
Sent: Friday, February 16, 2001 2:51 AM

> That follows because there are no unconstrained enumeration subtypes. The
> interesting part of the paragraph says:

Randy, I believe that this statement is incorrect, as are the conclusions
that you are drawing from it.

If T is an enumeration subtype, T'Base is unconstrained, see RM95 3.5(15).

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

From: Randy Brukardt
Sent: Friday, February 16, 2001 9:18 PM

Argghh, you're right of course. I always forget about 'Base. OTOH, there doesn't
seem to be much benefit to an unconstrained enumeration type. It just messes up
the rules.

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

From: Tucker Taft
Sent: Saturday, February 17, 2001 1:07 PM

I believe one reason 'Base was defined as unconstrained was so that
enumeration types would be more like integer types, where the
base subtype is unconstrained, and the first subtype is constrained.
This also makes the wording in 4.9 work for enumeration subtypes,
which says that a static subtype is a subtype with a static constraint
applied to a static unconstrained (base) subtype.

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

From: Randy Brukardt
Sent: Monday, February 19, 2001 3:22 PM

That's a circular argument, of course. The reason 4.9's wording works is because
we have unconstrained enumeration subtypes, and the reason we have unconstrained
enumeration subtypes is because we need the wording of 4.9 to work. :-)

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

From: Tucker Taft
Sent: Friday, February 16, 2001 8:32 AM

"Baird, Steve" wrote:
>
> The following is a list of some of the language issues that would
> have to be addressed if enumeration type extensions were to be
> added to the language (an action which I do not endorse).
>
> No one of these would be particularly difficult to deal with,
> but this is just a sampling and undoubtedly there would be others.
>
> The point I am trying to make is that this would not be a "simple"
> change.
>
>   1) How would stream-oriented attributes behave, both in the case
>      where the corresponding attribute of the parent type has the
>      default implementation and in the case where it has a
>      user-defined implementation?

I don't see the need for a special case here.  RM2000 13.13.2(8/1/1,25/1)
say user-defined stream attributes are inherited for untagged
derived types.  This seems like the simplest choice, since this
just treats these attributes like any other user-defined
primitive operation.  I can't imagine any kind of automatic
"extension" the compiler could provide.  For tagged extensions,
it is pretty clear how to construct an "extended" version of
the stream attributes (i.e. use the user-defined op for the
parent part, and use the default for the extension part).

>
>   2) It seems like the parent subtype must be unconstrained.
>      What would this mean
>           type E1 is (Aa, Bb, Cc);
>           subtype S is E1 range Aa .. Bb;
>           type E2 is new S with (Dd, Ee);
>      ?

The normal rules for type derivation say that you create
a derived type based on the parent type, and then a derived
subtype based on the parent subtype.  This would work the same here,
making the above equivalent to:

    type E2_Base is new E1 with (Dd, Ee);
    subtype E2 is E2_Base range Aa .. Bb;

Again, I don't see the need for a special case here.

>
>   3) Perhaps it falls out, but clarification might be needed
>      to ensure that a case like
>
>          type E1 is (Aa, Bb, Cc);
>          type Aa_Again is new E1 with (Dd, Aa);
>
>      is rejected (i.e. the explicit-takes-precedence-over-implicit
>      rule would not apply here).

This probably does need a special case, if we want
to disallow it.  Otherwise, it would be interpreted
as the inherited Aa is being overridden with a new one,
which isn't the end of the world, but is probably a mistake
that the user would like notification of.  We currently
explicitly disallow duplicate names in record extensions,
and perhaps the same would be appropriate here.


>
>      For similar reasons, enumeration extension of a formal derived
>      (enumeration) type should be disallowed.

I'm not convinced this is necessary.  I would treat it like
record extensions, where it is OK in the body, but in the spec,
you must check at instantiation time that there are no
duplicates (presuming we decide to disallow duplicates at all).
The definition of Enum'Value would have to indicate which
one is chosen (presumably the latter one, since that is
the only one the body "knows" about).

>
>   4) 4.6(28) "...this can only happen on conversion to a modular
>      type" would require modification. It might be worth stating
>      explicitly that extension values of two structurally similar
>      extensions are not corresponding values. For example,
>          type E1 is (Aa, Bb, Cc);
>          type E2 is new E1 with (Dd);
>          type E3 is new E1 with (Dd);
>          X3 : E3 := E3 (E2'(Dd));
>      the elaboration of X3 would raise Constraint_Error.

As I suggested in an earlier note, I would recommend requiring that
the type conversion perform a check against the base range of
the nearest common ancestor, followed by a range check against
the target subtype.

>
>   5) 3.4(9) and 13.1(5) would obviously require modification.
>      Consider, for example, the attributes LAST and SIZE.

Randy guessed you meant 13.1(15) which makes sense.  He also
pointed out that 'Size is subtype specific, but it still seems
that if 'Size is specified for the parent (first) subtype, then
that specified 'Size would be inherited by the extended (first)
subtype, and it will need to be overridden if it is too small.

>
>   6) This example should clearly be rejected,
>
>          type E1 is (Aa, Bb, Cc);
>          generic
>              type E2 is new E1;
>          package G is
>              Static_Value : constant := E2'Pos (E2'Base'Last);
>          end G;
>          type E3 is new E1 with (Dd, Ee);
>          package I is new G (E2 => E3);

I agree with your suggestion that we should make all scalar formal
types non-static, rather than just "formal scalar types".

I was wondering whether we should require a special formal
type syntax for enumeration extensions, e.g:
   type Possibly_Extended is new Base with (<>);

However, this is probably unnecessary because the actual for
a formal derived type could be a constrained subtype, so you
can't make assumptions about 'First or 'Last anyway inside
the generic.  As suggested above, 'Base'First or 'Base'Last
might not be what is expected, but that seems OK.
In fact, derived enumeration types are pretty rare anyway,
so chances are all uses of formal derived enumeration types
would be there primarily to deal with extensions. (This reminds
me of the Ada 83 => Ada 95 situation, where derived types
were pretty rare in Ada 83, and only became of widespread use
once (tagged) extension was introduced.)

>      but on what basis? Does the definition of "static scalar
>      subtype" need to be changed (i.e. treat a formal derived
>      scalar type the same as a formal scalar type in 4.9(26)), or
>      should the matching rules of 12.5.1(7-10) be tightened up?

Yes, I agree we should make all scalar formal types non static.

>
>   7) Would 3.4(19) require modification? Is it already clear
>      (with no wording changes) that in this example
>          package Pkg1 is
>              type E1 is (Aa, Bb, Cc);
>              procedure Proc (X : E1);
>          end Pkg;
>          package Pkg2 is
>              type E2 is new Pkg1.E1 with (Dd, Ee);
>          end;
>      the subtype of Pkg2.Proc.X would have 3 values, not 5?

Good point.  I claimed there was no special case here, but
now it looks like if we derive from a subtype whose range
matches the base range, then the first subtype of the
extended enumeration type should also have a range that
matches the base range.  On the other hand, if the parent
subtype's range does not match the base range, then the
derived subtype should have that same reduced range.

Alternatively, we could say if the 'Last of the parent
subtype equals 'Base'Last, then the 'Last of the derived
subtype equals the new 'Base'Last.  This could be marginally
more useful.

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

From: Michael Yoder
Sent: Friday, February 16, 2001 1:11 PM

Tucker wrote:

> >
> >   3) Perhaps it falls out, but clarification might be needed
> >      to ensure that a case like
> >
> >          type E1 is (Aa, Bb, Cc);
> >          type Aa_Again is new E1 with (Dd, Aa);
> >
> >      is rejected (i.e. the explicit-takes-precedence-over-implicit
> >      rule would not apply here).
>
>This probably does need a special case, if we want
>to disallow it.  Otherwise, it would be interpreted
>as the inherited Aa is being overridden with a new one,
>which isn't the end of the world, but is probably a mistake
>that the user would like notification of.  We currently
>explicitly disallow duplicate names in record extensions,
>and perhaps the same would be appropriate here.

I'd recommend disallowing a duplicate value; otherwise, T'Value (T'Image
(X)) = X fails.  Or, put another way, I think it's best to preserve the
property that T'Image maps to distinct values always.

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

From: Randy Brukardt
Sent: Friday, February 16, 2001 9:38 PM

Tuck wrote, responding to Steve Baird:

> >   1) How would stream-oriented attributes behave, both in the case
> >      where the corresponding attribute of the parent type has the
> >      default implementation and in the case where it has a
> >      user-defined implementation?
>
> I don't see the need for a special case here.  RM2000 13.13.2(8/1/1,25/1)
> say user-defined stream attributes are inherited for untagged
> derived types.  This seems like the simplest choice, since this
> just treats these attributes like any other user-defined
> primitive operation.  I can't imagine any kind of automatic
> "extension" the compiler could provide.  For tagged extensions,
> it is pretty clear how to construct an "extended" version of
> the stream attributes (i.e. use the user-defined op for the
> parent part, and use the default for the extension part).

It's annoying that Tucker and I got exactly opposite answers to most of
these questions. Seems to make Steve's point. :-)

The parent type's user-defined stream attributes may not be prepared to
handle the extra literals added by an extension. If you inherit the parent
type's attributes, they simply might break when expected to handle those
literals. I still think the best solution is to revert them.

> >   2) It seems like the parent subtype must be unconstrained.
> >      What would this mean
> >           type E1 is (Aa, Bb, Cc);
> >           subtype S is E1 range Aa .. Bb;
> >           type E2 is new S with (Dd, Ee);
> >      ?
>
> The normal rules for type derivation say that you create
> a derived type based on the parent type, and then a derived
> subtype based on the parent subtype.  This would work the same here,
> making the above equivalent to:
>
>     type E2_Base is new E1 with (Dd, Ee);
>     subtype E2 is E2_Base range Aa .. Bb;
>
> Again, I don't see the need for a special case here.

Err, then you have to have a special case for the first subtype: otherwise,
you'd end up with a constraint that didn't allow the new literals.

I still prefer to simply ignore the constraint (all derived types ought to
do that, but that's another story).

> >   3) Perhaps it falls out, but clarification might be needed
> >      to ensure that a case like
> >
> >          type E1 is (Aa, Bb, Cc);
> >          type Aa_Again is new E1 with (Dd, Aa);
> >
> >      is rejected (i.e. the explicit-takes-precedence-over-implicit
> >      rule would not apply here).
>
> This probably does need a special case, if we want
> to disallow it.  Otherwise, it would be interpreted
> as the inherited Aa is being overridden with a new one,
> which isn't the end of the world, but is probably a mistake
> that the user would like notification of.  We currently
> explicitly disallow duplicate names in record extensions,
> and perhaps the same would be appropriate here.

OK, I added a sentence to that effect to the AI.

> >      For similar reasons, enumeration extension of a formal derived
> >      (enumeration) type should be disallowed.
>
> I'm not convinced this is necessary.  I would treat it like
> record extensions, where it is OK in the body, but in the spec,
> you must check at instantiation time that there are no
> duplicates (presuming we decide to disallow duplicates at all).
> The definition of Enum'Value would have to indicate which
> one is chosen (presumably the latter one, since that is
> the only one the body "knows" about).

How do you assign the position numbers of the literals in the body? And in
both cases, the position numbers of the literals aren't static (using my
"generic code sharing" glasses). Best to disallow these.


> >   5) 3.4(9) and 13.1(5) would obviously require modification.
> >      Consider, for example, the attributes LAST and SIZE.
>
> Randy guessed you meant 13.1(15) which makes sense.  He also
> pointed out that 'Size is subtype specific, but it still seems
> that if 'Size is specified for the parent (first) subtype, then
> that specified 'Size would be inherited by the extended (first)
> subtype, and it will need to be overridden if it is too small.

I suppose.

> >   7) Would 3.4(19) require modification? Is it already clear
> >      (with no wording changes) that in this example
> >          package Pkg1 is
> >              type E1 is (Aa, Bb, Cc);
> >              procedure Proc (X : E1);
> >          end Pkg;
> >          package Pkg2 is
> >              type E2 is new Pkg1.E1 with (Dd, Ee);
> >          end;
> >      the subtype of Pkg2.Proc.X would have 3 values, not 5?
>
> Good point.  I claimed there was no special case here, but
> now it looks like if we derive from a subtype whose range
> matches the base range, then the first subtype of the
> extended enumeration type should also have a range that
> matches the base range.  On the other hand, if the parent
> subtype's range does not match the base range, then the
> derived subtype should have that same reduced range.

Tuck seems to have this backwards: for the declaration of a type, we don't
want to inherit the subtype. However, for this case (a derived subprogram
parameter), we *do* want to inherit the subtype. (Which is what happens
without modifying 3.4(19)). That's because the inherited subprogram probably
isn't prepared to deal with the extra literals; any call using them should
raise Constraint_Error (see my large example).

However, there is a (minor) problem if someone actually uses 'Base:

   package Pkg1 is
	... as before...
      procedure Proc (X : E1'Base);
   end Pkg1;
   package Pkg2 is
      type E2 is new Pkg1.E1 with (Dd, Ee);
   end Pkg2;

In this case, Proc would indeed get all of the literals, and that might be
surprising. I claim that if someone actually uses the unconstrained type,
they must have wanted anything possible, and thus we still don't need to
change the rule.

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

From: Robert A Duff
Sent: Friday, February 16, 2001 10:03 AM

I believe according to the current wording, an extension of Boolean
would be a boolean type, so the following would be legal:

    type My_Boolean is new Boolean with (Maybe, Perhaps, Probably);

    X: My_Boolean := Maybe;

    if X then ...

but it's not completely clear what it should do at run time.  ;-)

There are other special things about boolean types that would need to be
investigated.  Eg, does an array of one of these things get "xor"?

Is this legal:

    package P is
	type T1 is (Red, Blue);
	type T2 is new T1 with private;
    private
	type T2 is new T1 with (Green);
    end P;

?

I'll repeat what I said before: This feature is not near the top of the
priority list, IMHO.

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

From: Michael Yoder
Sent: Friday, February 16, 2001 2:21 PM

>but it's not completely clear what it should do at run time.  ;-)

I'd say it propagates Constraint_Error; of course I'm reasoning by
analogy.  Alternatively, treat all non-False values as true.

>There are other special things about boolean types that would need to be
>investigated.  Eg, does an array of one of these things get "xor"?

Well, presumably they map whatever these functions become for the type
itself.  How about:

     type T is new Boolean with (Another, ...);
     -- X and Y = T'Min (X, Y)
     -- X or Y = T'Max (X, Y)
     -- X xor Y = T'Val ((T'Pos (X) + T'Pos(Y)) mod N) where
     --      N = 1 + T'Pos (T'Last)
     -- not X = T'Val (N - 1 - T'Pos (X))
Alternatively,
     -- not False = True
     -- not True = False
     -- not X = X otherwise

This last definition of "not" may seem surprising, but my recollection is
that it is used in some multivalued logics.  The first definition of "not"
preserves de Morgan's rules.


>Is this legal:
>
>     package P is
>         type T1 is (Red, Blue);
>         type T2 is new T1 with private;
>     private
>         type T2 is new T1 with (Green);
>     end P;

I'd assume so.  You may now cry "Aha!" and spring some horrible consequence
on us if you like.  :-)

>I'll repeat what I said before: This feature is not near the top of the
>priority list, IMHO.

I agree; but so far I think this issue is generating effort rather than
soaking it from other activities.  I hope the discussion will continue
until there is a complete proposal.  We can choose to shelve it until
triage time if people like.

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

From: Robert A Duff
Sent: Friday, February 16, 2001 5:10 PM

> I'd say it propagates Constraint_Error; of course I'm reasoning by
> analogy.  Alternatively, treat all non-False values as true.

Hmm.  My first thought was to say that My_Boolean is not a "boolean
type", so it's illegal to use it in if_stmts, and it doesn't get an
"xor" operator, and so forth.  But depending no how Tuck's argument with
Steve about generic formal parameters turns out, that might cause
contract model problems to rear their ugly heads.

> >There are other special things about boolean types that would need to be
> >investigated.  Eg, does an array of one of these things get "xor"?
>
> Well, presumably they map whatever these functions become for the type
> itself.  How about:
>
>      type T is new Boolean with (Another, ...);
>      -- X and Y = T'Min (X, Y)
>      -- X or Y = T'Max (X, Y)
>      -- X xor Y = T'Val ((T'Pos (X) + T'Pos(Y)) mod N) where N = 1 + T'Pos
> (T'Last)
>      -- not X = T'Val (N - 1 - T'Pos (X))
> Alternatively,
>      -- not False = True
>      -- not True = False
>      -- not X = X otherwise
>
> This last definition of "not" may seem surprising, but my recollection is
> that it is used in some multivalued logics.  The first definition of "not"
> preserves de Morgan's rules.

The above remind me of the definition of "not" for non-binary modular
types.

> >Is this legal:
> >
> >     package P is
> >         type T1 is (Red, Blue);
> >         type T2 is new T1 with private;
> >     private
> >         type T2 is new T1 with (Green);
> >     end P;
>
> I'd assume so.  You may now cry "Aha!" and spring some horrible consequence
> on us if you like.  :-)

No, I didn't have any nasty thing up my sleeve.  But the syntactic
proposals I've seen so far didn't include any mention of "with private",
but it does seem natural to allow it.  It might even be useful (an enum
type with some values hidden).

> >I'll repeat what I said before: This feature is not near the top of the
> >priority list, IMHO.
>
> I agree; but so far I think this issue is generating effort rather than
> soaking it from other activities.  I hope the discussion will continue
> until there is a complete proposal.  We can choose to shelve it until
> triage time if people like.

Besides, this is fun.

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

From: Randy Brukardt
Sent: Friday, February 16, 2001 10:07 PM

> > Bob Duff wrote:
> >
> > At 11:02 AM 2/16/01 -0500, you wrote:
> > >I believe according to the current wording, an extension of Boolean
> > >would be a boolean type, so the following would be legal:
> > >
> > >     type My_Boolean is new Boolean with (Maybe, Perhaps, Probably);
> > >
> > >     X: My_Boolean := Maybe;
> > >
> > >     if X then ...
> > >
> > >but it's not completely clear what it should do at run time.  ;-)
> >
> > I'd say it propagates Constraint_Error; of course I'm reasoning by
> > analogy.  Alternatively, treat all non-False values as true.
>
> Hmm.  My first thought was to say that My_Boolean is not a "boolean
> type", so it's illegal to use it in if_stmts, and it doesn't get an
> "xor" operator, and so forth.  But depending no how Tuck's argument with
> Steve about generic formal parameters turns out, that might cause
> contract model problems to rear their ugly heads.

If the Corrigendum hadn't changed all of the Boolean operations to 'Base to
fix some other problem, the Constraint_Error solution would fall out
automatically.

Extensions of generic scalar formal parameters will not be allowed. Mr.
Code-shared generics forbids it. :-)

Are you thinking of something like:

    generic
       type Some_Bool is new Boolean;
    package Gen is
       procedure Something;
       B : Some_Bool := Some_Bool'Last;
    end Gen;

    package body Gen is
       procedure Something is
       begin
           if B then -- Use of B as a Boolean type.
              ...
           end if;
       end Something;
    end Gen;

    type Ext is new Boolean with (Whatever);

    package A_Gen is new Gen (Ext);

Yup, that's a problem, because we're doing an if on a Boolean type, and it's
hidden in the body. Our options would be:
   (1) Disallow the instantiation (if the formal is a Boolean type, then the
actual must be a Boolean type).
   (2) Say that the formal is not a Boolean type (disallowing the IF).
   (3) Push the check to runtime (raising Constraint_Error as Mike
suggested).
   (4) Defining the meaning of "if <cond> then" to be "if <cond> /= False
then". (And then using the rules for modular types to define all of the
other logical operations.)

I don't know which makes the most sense of these. (2) might be incompatible
with somebodies program, but it's hard to imagine what they would be doing
where they'd need a derived Boolean type. None of the others have an
compatibility problem at all.

> > I agree; but so far I think this issue is generating effort rather than
> > soaking it from other activities.  I hope the discussion will continue
> > until there is a complete proposal.  We can choose to shelve it until
> > triage time if people like.
>
> Besides, this is fun.

Well, it probably is taking some of my time away from other tasks, so it
isn't completely harmless.

But it is fun. :-)

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

From: Robert A Duff
Sent: Saturday, February 17, 2001 9:30 AM

> Are you thinking of something like:
>
>     generic
>        type Some_Bool is new Boolean;
>     package Gen is
>        procedure Something;
>        B : Some_Bool := Some_Bool'Last;
>     end Gen;
>
>     package body Gen is
>        procedure Something is
>        begin
>            if B then -- Use of B as a Boolean type.
>               ...
>            end if;
>        end Something;
>     end Gen;
>
>     type Ext is new Boolean with (Whatever);
>
>     package A_Gen is new Gen (Ext);

Yes, that's the sort of thing I was thinking about.

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

From: dewar@gnat.com
Sent: Sunday, February 18, 2001 7:59 AM

All this discussion of extensible enumerated types is very nice, but
surely it simply shows this facility is not worth pursuing at this
stage???

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

From: Randy Brukardt
Sent: Monday, February 19, 2001 2:23 PM

But it's also obvious that people are interested in working on this feature,
which is more than I can say for any number of so-called "more important"
proposals. I think it makes the most sense for the full ARG to decide
whether they want to persue this at the next meeting. It can't possibly be
harmful to tease out as many problems as possible: at worst, it will provide
a ready answer of "too complex" the next time someone brings it up.

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

From: Deller, Steve
Sent: Monday, February 19, 2001 12:13 AM

I for one believe extensible enumerated types would have seen frequent use
already, had they been available.

To me that makes a strong argument for this being a very useful facility and
therefore one worth pursuing at this time.

The POSIX Ada committee tried to make POSIX.Error_Code and POSIX.Signal_Code
enumerated types, but the requirement for independent extension kept raising
its ugly head and in the end, we settled for a distinctly non-Ada
"implementation defined integer type" with loads of defined constants.

I have seen similar situations repeated with numerous applications, where
there is some base list of enumerations that must be allowed to be extended
independently.  The typical solution is to drop back to integers and play
"other-language"-like games in Ada.

As for Booleans, an extended Boolean is not a Boolean and should not be
permitted in an if statement.  The alternative, as many have noted, is too
gruesome to contemplate.

For utility, it seems to me that if extensible enumeration types are
defined, then there needs to be some way to define a formal for generics
that explicitly *disallows* or *allows* extension of a derived enumeration
type.  If I understood Pascal, that also solves the Boolean-generic issue.

Extended Booleans are disallowed as expression results in an if statement.
By extension :-),
  generic
    type X is new Boolean ;
    type Y is new Boolean with (<>) ;

means  X can be used in if statement expression, and Y cannot.

Of course you can always do:
  B : Y ;
  ...
  if Boolean(B) then ...

and get a constraint error if B is neither True nor False.

Below is one way I believe we might have used extensible enumeration types
with POSIX.

Regards,
Steve


package Posix is
  type Error_Code is (No_Error, Argument_Too_Long, File_Does_Not_Exist ) ;
end Posix ;

with Posix ;
package Posix_HPUX_Extensions is
  type Error_Code is new Posix.Error_Code with ( Math_Domain_Error ) ;
end Posix_HPUX_Extensions ;

with Posix_HPUX_Extensions ;
package Posix_HPUX_10_0_Extensions is
  type Error_Code is new Posix_HPUX_Extensions.Error_Code with ( No_Thread )
;
end Posix_HPUX_Extensions ;

with Posix ;
package Posix_Solaris_Extensions is
  type Error_Code is new Posix.Error_Code with ( Thread_Stopped ) ;
end Posix_Solaris_Extensions ;


-- Define a generic that can be instantiated with any "Error_Code" type
with Posix ;
generic
  type P_E is new Posix.Error_Code with (<>) ;
procedure Portable_Announce_Error ( Error : in P_E ) ;

procedure Portable_Announce_Error ( Error : in P_E ) is
    type Message is new String(1..43) ;

    Known_Messages : array ( Posix.Error_Code'range ) of Message :=
        ( No_Error =>            "There was no error                         " ,
          Argument_Too_Long =>   "An argument is too long for call buffer    " ,
          File_Does_Not_Exist => "The file is not in the specified directory " ) ;

    Print_Message : Message :=   "Non-standard POSIX error code              " ;

begin
    case Error is
      when P_E'Parent'Range => Print_Message :=
           Known_Messages(Posix.Error_Code( Error)) ;
      -- ?? Not sure 'Parent'Range has appropriate typing
      -- ?? May need to use: when P_E( Posix.Error_Code'First) ..
      --        P_E( Posix.Error_Code'Last) =>
      when others => null ;
    end case ;

    Text_Io.Put_Line ( "Error: " & P_E'Image(Error) & ", " & Print_Message );

end Announce_Error;

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

From: Ted Baker [baker@dad.cs.fsu.edu]
Sent: Monday, February 19, 2001 4:57 PM

| The POSIX Ada committee tried to make POSIX.Error_Code and POSIX.Signal_Code
| enumerated types, but the requirement for independent extension kept raising
| its ugly head and in the end, we settled for a distinctly non-Ada
| "implementation defined integer type" with loads of defined constants.

As an original member and later working group chair of the POSIX
Ada bindings projects, I disagree with the conclusion above.  That
is, we *thought* we wanted extensible enumeration types back when
we did the original POSIX.5 binding, but now that we have gone
through two revisions and extensions, and I have experience implementing
and using the API, I don't believe it any more.

Steve's suggested example looks very nice, but would not work in
practice, because the POSIX error codes are *really* integers.  The
OS implementation is always free to set additional error codes,
that are not given names in the C-language API header files.  So,
one were to use an Ada enumeration type to represent such values
the binding would not know what to do with values that do not have
names, when the OS sets them into errno.

I am also uncomfortable about the fact that with an enumeration
type in an API one begs programmers to use it fully, e.g., ranges,
expressions using relational operators, and subranges in array
declarations, aggregates, and case statements.  So long as
extensions must be at the end of the range, these may seem fairly
tame, but one must still be wary of code that uses of 'Last and
'First breaking, since the specific meaning will change when the
type is extended.

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

From: dewar@gnat.com
Sent: Monday, February 19, 2001 5:45 PM

I definitely agree with Ted's conclusion here

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

From: Randy Brukardt
Sent: Monday, February 19, 2001 7:49 PM

Ted said:

<<I am also uncomfortable about the fact that with an enumeration
type in an API one begs programmers to use it fully, e.g., ranges,
expressions using relational operators, and subranges in array
declarations, aggregates, and case statements.  So long as
extensions must be at the end of the range, these may seem fairly
tame, but one must still be wary of code that uses of 'Last and
'First breaking, since the specific meaning will change when the
type is extended.>>

Since an extension creates a new type, it certainly cannot be the case that
existing code is broken by that type. Of course, someone could try to reuse
existing code that wasn't "extension-safe", but that is always a problem
with extensions of any kind. I don't see anything new or surprising or
dangerous here.

(Of course, the specific example appeared to be a case where using an
enumeration type might have been a mistake. But that hardly could mean that
*all* uses of enumeration types are a mistake!!)

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

From: Deller, Steve
Sent: Tuesday, February 20, 2001 2:29 PM

Ted,
Nice to hear from you :-).

> I am also uncomfortable about the fact that with an enumeration
> type in an API one begs programmers to use it fully, e.g., ranges,
> expressions using relational operators, and subranges in array
> declarations, aggregates, and case statements.  So long as
> extensions must be at the end of the range, these may seem fairly
> tame, but one must still be wary of code that uses of 'Last and
> 'First breaking, since the specific meaning will change when the
> type is extended.

I do not understand this.  I thought 'First and 'Last allowed one to write a
program that worked when type ranges were extended.  I would *expect* and
*depend* on 'First and 'Last changing.  Also, I thought
POSIX.Error_Enumeration'First and POSIX.Error_Enumeration'Last would *not*
change just because someone defined a type extension, but would change only
if code defining that type changed.

What am I missing?

> Steve's suggested example looks very nice, but would not work in
> practice, because the POSIX error codes are *really* integers.  The
> OS implementation is always free to set additional error codes,
> that are not given names in the C-language API header files.  So,
> were one to use an Ada enumeration type to represent such values
> the binding would not know what to do with values that do not have
> names, when the OS sets them into errno.

While the C interface defines errors as integers, I disagree that they are
*really* integers.  I believe they are enumerated errors, some of which we
do not know the enumeration *yet*.  There is no arithmetic defined on these
errors.  I have yet to see where one gets a "count" out of an error code.

There are three categories of error codes:
  1. POSIX required -- all POSIX systems support these
  2. Known extensions -- we know what these error codes mean
  3. Unknown extensions -- we have no idea what these error codes mean

I, for one, would welcome an application that clearly distinguished these
categories.  Or rather, an API that forced a straightforward application to
distinguish "automatically" these three categories.  And I happen to think
that extensible enumeration types would have provided the appropriate
mechanism to write such an API.

Yes, the value returned by the OS has to be numeric.  But POSIX could have
provided an extensible enumeration type for all errors that are core errors,
and provided a function for converting an OS value to any extension of that
POSIX enumeration type.

For the case where a returned numeric error code is not convertible to the
(possibly extended) error code enumeration, the application pretty much has
to bail.  After all, the meaning of the error is, by definition, unknown.

I can think of several ways to handle unknown returned values in an API:
exception, special value "UNKNOWN", boolean test for "known error".  I can
also envision programs that could handle *any* extension of the POSIX
enumeration so it worked "correctly" for all known errors.  And, when the
meaning of "exceptional"/"unknown" error values became known, it would be a
simple matter to extend the enumeration type appropriately.

With POSIX.Error_Code as it is now, I do not know, when I see something of
POSIX.Error_Code, whether the application code is trying to handle only core
POSIX errors, Solaris POSIX errors, HPUX errors, and so on.

With extensible enumeration types, I could see where code was POSIX-core
only, and where it might be handling OS-specific extensions.  That
information would be supremely helpful when porting an application from one
OS to another.

Anyway, I would have had fun arguing with you about the relative merits,
since I always felt I came away with a better understanding of the issues.

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

From: Randy Brukardt [Randy@RRSoftware.Com]
Sent: Monday, February 19, 2001 2:41 PM
To: 'Ada-Comment List'
Cc: 'Deller, Steve'
Subject: Re: [Ada-Comment] Extensible Enumerated Types

> For utility, it seems to me that if extensible enumeration types are
> defined, then there needs to be some way to define a formal for generics
> that explicitly *disallows* or *allows* extension of a derived enumeration
> type.  If I understood Pascal, that also solves the Boolean-generic issue.

Yes, it would, but...

> Extended Booleans are disallowed as expression results in an if statement.
> By extension :-),
>   generic
>     type X is new Boolean ;
>     type Y is new Boolean with (<>) ;
>
> means  X can be used in if statement expression, and Y cannot.

The "problem" with this is that the first form is almost completely useless.
The only reason to add all of the baggage of a new kind of generic formal is
to allow the 100% compatibility with the likely to be zero existing programs
that use formals in the form of X above.

I think it makes more sense to say that a generic derived formal type is
never a Boolean type. The only program that that could break is one that
uses both derived Boolean types *and* generic formal derived Boolean
types -- I think that is the empty set.

(Besides, that would eliminate the major problem with supporting
representation clauses on derived Boolean types.)

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

From: dewar@gnat.com
Sent: Sunday, February 18, 2001 9:09 AM

<<The above remind me of the definition of "not" for non-binary modular
types.>>

Indeed! One of the most horrible features of the language in my opinion.
Orthogonality gone berserk :-)

But then the whole presence of non-binary modular types is a mistake
in my opinion (and for sure this feature only appeared in Ada 95
through a misunderstanding -- the designers thought the URG had
recommended this feature, when in fact they had overwhelmingly
rejected it :-)

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

From: dewar@gnat.com
Sent: Sunday, February 18, 2001 8:46 AM
To: ada-comment@ada-auth.org
Subject: Re: [Ada-Comment] Extensible Enumerated Types

<<I'd say it propagates Constraint_Error; of course I'm reasoning by
analogy.  Alternatively, treat all non-False values as true.>>

I really think that's horrible. Why add this completely unnecessary and
useless feature for the purposes of misguided orthogonality.

<<This last definition of "not" may seem surprising, but my recollection is
that it is used in some multivalued logics.  The first definition of "not"
preserves de Morgan's rules.>>

Surprising indeed, again, why confuse things.

Treat Boolean specially and don't allow extension. After all we don't
require even that rep clauses for Boolean be supported.

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

From: Pascal Leroy
Sent: Saturday, February 17, 2001 5:04 AM

> > Alternatively, treat all non-False values as true.

Boy, that's disgusting.  I am told there are programming languages out there
which treat non-zero values as True.  Do we really want to do the same?

> Hmm.  My first thought was to say that My_Boolean is not a "boolean
> type", so it's illegal to use it in if_stmts, and it doesn't get an
> "xor" operator, and so forth.

That seems like the best approach.

> But depending no how Tuck's argument with
> Steve about generic formal parameters turns out, that might cause
> contract model problems to rear their ugly heads.

Not if you have a new kind of generic formal parameter for an extended
enumeration type.

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

From: Michael Yoder
Sent: Monday, February 19, 2001 4:58 PM

> Hmm.  My first thought was to say that My_Boolean is not a "boolean
> type", so it's illegal to use it in if_stmts, and it doesn't get an
> "xor" operator, and so forth.

That's best if it doesn't break anything.

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

From: dewar@gnat.com
Sent: Monday, February 19, 2001 5:43 PM

I think it is more reasonable to just make it illegal. Of what possible
use is this extended boolean that is not a boolean?

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

From: Randy Brukardt [Randy@RRSoftware.Com]
Sent: Monday, February 19, 2001 7:34 PM

It allows a different set of contract model violations. :-)

That is, you have to prohibit some operations in generics if you adopt
either of these rules. Exactly which ones are different for the two rules.

One could use an extension of a Boolean to make a three-valued logic, where
the user provided the appropriate operations. But I don't find that very
compelling.

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

From: Michael Yoder
Sent: Monday, February 19, 2001 6:06 PM

Forbidding extensions to Boolean would be tolerable to me, since if we
forbid extensions of enumeration types in generic bodies the contract model
problems with this go away.

If extensions of Boolean were for multivalued logic, which is the only
useful reason I can think of for making such extensions, you would probably
want the new values to be *between* False and True.  That would be a bit
much.  On the other hand, the user could redefine "<" etc. to achieve this.

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

From: dewar@gnat.com
Sent: Sunday, February 18, 2001 10:02 AM

<<Boy, that's disgusting.  I am told there are programming languages out there
which treat non-zero values as True.  Do we really want to do the same?>>

Just as a side comment, GNAT actually implements zero/non-zero semantics
for boolean types which have appropriate foreign conventions. We found
we had to do this for the Fortran LOGICAL case (some would argue that
making Fortran LOGICAL new Boolean when it has zero/non-zero semantics
is a language design error -- or at least an oversite), so why not do
it for C "boolean" as well. This has actually turned out quite useful,
and is perhaps worth an AI.

I wonder if other compilers properly handle the zero/non-zero semantics
of Interfaces.Fortran.Logical? We had to fix this not because of an
ACATS test (there is none, which is a big omission), but because of
actual user complaints with interfacing to Fortran.

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

From: Randy Brukardt [Randy@RRSoftware.Com]
Sent: Monday, February 19, 2001 2:26 PM
To: 'Ada-Comment List'
Subject: Re: [Ada-Comment] Extensible Enumerated Types

> I wonder if other compilers properly handle the zero/non-zero semantics
> of Interfaces.Fortran.Logical? We had to fix this not because of an
> ACATS test (there is none, which is a big omission), but because of
> actual user complaints with interfacing to Fortran.

Janus/Ada does (at least, it is *supposed* to; I don't know if anyone ever
has used the Fortran interface in J/A). We mainly did that for C so that the
Windows interface could use Boolean directly rather than having to define
another type to represent that. Dunno if that was a good idea.

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

From: dewar@gnat.com
Sent: Monday, February 19, 2001 4:57 PM

I think it's a reasonable idea, it is exactly what GNAT does if you have
a derived Boolean with a convention of C.

I don't see that you can use Standard.Boolean, or are you saying you
implement zero/non-zero semantics for all booleans (that sounds expensive!)

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

From: Randy Brukardt
Sent: Monday, February 19, 2001 5:19 PM

We use zero/non-zero semantics for Standard.Boolean when used in interfaced
subprograms (not in the Ada code), so that any value other than 0 or 1 gets
converted to 1 as part of the call (or return from call, for functions). And
we use zero/non-zero semantics in all conditionals (which rarely costs
extra).

That won't work for uses of Boolean in convention C data types, created by C
code, where the value is then used a normal logical operator (And Then and
Or Else are really conditional control structures, so they work). So our
implementation is not quite complete; as I said, no one has ever complained.
(And you are correct that it would be too expensive to do that for standard
Boolean for all operations.)

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

From: dewar@gnat.com
Sent: Monday, February 19, 2001 7:18 PM

OK, I see, I think I prefer the GNAT approach which is to do this only
for derived booleans with convention C (or Fortran), and then to do it
uniformly.

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

From: Randy Brukardt
Sent: Monday, February 19, 2001 7:25 PM

I agree. I wish I'd have thought of that at the time. (Too late now to
change of course, lots of code depends on it.)

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

From: Tucker Taft
Sent: Saturday, February 17, 2001 1:23 PM

Randy Brukardt wrote:
>
> Tuck wrote, responding to Steve Baird:
>
> > >   1) How would stream-oriented attributes behave, both in the case
> > >      where the corresponding attribute of the parent type has the
> > >      default implementation and in the case where it has a
> > >      user-defined implementation?
> >
> > I don't see the need for a special case here.  RM2000 13.13.2(8/1/1,25/1)
> > say user-defined stream attributes are inherited for untagged
> > derived types.  This seems like the simplest choice, since this
> > just treats these attributes like any other user-defined
> > primitive operation.  I can't imagine any kind of automatic
> > "extension" the compiler could provide.  For tagged extensions,
> > it is pretty clear how to construct an "extended" version of
> > the stream attributes (i.e. use the user-defined op for the
> > parent part, and use the default for the extension part).
>
> It's annoying that Tucker and I got exactly opposite answers to most of
> these questions. Seems to make Steve's point. :-)
>
> The parent type's user-defined stream attributes may not be prepared to
> handle the extra literals added by an extension. If you inherit the parent
> type's attributes, they simply might break when expected to handle those
> literals. I still think the best solution is to revert them.

They don't really break, presumably, because their semantics
are defined in terms of conversion, and that would "weed out"
any of the values outside the base range of the parent type.
This is going to be true for all user-defined primitives.
Perhaps they should all become abstract, requiring that they
all be overridden, since the implicit conversion to the parent
is bound to cause trouble.  Curiously enough, primitive functions
that return the parent type and have no parameters of the type
(e.g. the enumeration literals) and primitive procedures that have
only OUT parameters of the type would be OK.  Any primitive with an
IN or IN OUT parameter of the type should perhaps become abstract in
an enumeration type extension.

Weird...
>
> > >   2) It seems like the parent subtype must be unconstrained.
> > >      What would this mean
> > >           type E1 is (Aa, Bb, Cc);
> > >           subtype S is E1 range Aa .. Bb;
> > >           type E2 is new S with (Dd, Ee);
> > >      ?
> >
> > The normal rules for type derivation say that you create
> > a derived type based on the parent type, and then a derived
> > subtype based on the parent subtype.  This would work the same here,
> > making the above equivalent to:
> >
> >     type E2_Base is new E1 with (Dd, Ee);
> >     subtype E2 is E2_Base range Aa .. Bb;
> >
> > Again, I don't see the need for a special case here.
>
> Err, then you have to have a special case for the first subtype: otherwise,
> you'd end up with a constraint that didn't allow the new literals.

Yes, I realized that later.

>
> I still prefer to simply ignore the constraint (all derived types ought to
> do that, but that's another story).

That seems pretty inconsistent.  I would rather complain
at the point of derivation than ignore the constraint on the
parent subtype.  I think the approach of imposing the same constraint
unless the parent subtype'Last = parent_subtype'Base'Last is
probably the best intermediate solution.

> ...
> How do you assign the position numbers of the literals in the body? And in
> both cases, the position numbers of the literals aren't static (using my
> "generic code sharing" glasses). Best to disallow these.

Yes, I suppose we could disallow extension of a formal enumeration
type in the body, in the same way we disallow extension of tagged
types in a body.

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

From: Randy Brukardt
Sent: Monday, February 19, 2001 3:14 PM

> They don't really break, presumably, because their semantics
> are defined in terms of conversion, and that would "weed out"
> any of the values outside the base range of the parent type.

You mean by raising Constraint_Error. Probably right.

> This is going to be true for all user-defined primitives.
> Perhaps they should all become abstract, requiring that they
> all be overridden, since the implicit conversion to the parent
> is bound to cause trouble.

Tuck, *please* forget that "abstract" stuff: "must-be-overridden" hasn't
been tied to abstract since Mapping 4.0. Indeed, "must-be-overridden"
primitives are *not* abstract.

As far as the idea itself, I think it is terrible. It would make these
things too annoying to use; there's no benefit in forcing people to make
things work perfectly when the language is safe in any case. I could see
doing that for stream attributes specifically, but not for all primitives.

> Curiously enough, primitive functions
> that return the parent type and have no parameters of the type
> (e.g. the enumeration literals) and primitive procedures that have
> only OUT parameters of the type would be OK.  Any primitive with an
> IN or IN OUT parameter of the type should perhaps become abstract in
> an enumeration type extension.
>
> Weird...

> > I still prefer to simply ignore the constraint (all derived types ought to
> > do that, but that's another story).
>
> That seems pretty inconsistent.  I would rather complain
> at the point of derivation than ignore the constraint on the
> parent subtype.  I think the approach of imposing the same constraint
> unless the parent subtype'Last = parent_subtype'Base'Last is
> probably the best intermediate solution.

Since Ada is a "name-equivalence" rather than a "structural-equivalence"
language, I would prefer to make the special-case rule apply only to the
first subtype itself, not to any accidentally matching subtypes.

Your rule would break various subtypes I've used in Claw:

    type System_Info_Kinds is
      (Icon_Spacing, Double_Click_Time, Screen_Width, Screen_Height);
    subtype Readable_System_Info_Kinds is
        range Double_Click_Time .. Screen_Height;
    subtype Writable_System_Info_Kinds is
        range Icon_Spacing .. Double_Click_Time;

    procedure Get_Info (Info_Kind : in Readable_System_Info_Kinds)
        return Claw.Int;

    ...

    type Windows_2000_System_Info_Kinds is new System_Info_Kinds
       with (Drag_Width, Drag_Height);

with Tucker's rule, the inherited Get_Info would include the new literals,
but that would be wrong (Drag_Height and Drag_Width are not readable here),
and in any case, the routine would not be expecting any extra literals.

To look at the results, let's look at all of the interesting cases:

     type Alpha is (A, B, C, D);
     subtype Head_Alpha is range A .. C;
     subtype Tail_Alpha is range B .. D;
     subtype All_Alpha  is range A .. D;

     ...

     type New1 is Alpha with (E, F); -- (1)
     type New2 is Head_Alpha with (E, F); -- (2)
     type New3 is Tail_Alpha with (E, F); -- (3)
     type New4 is All_Alpha with (E, F); -- (4)

What are the constraints of the first subtype? It's clear that for (1), we
have to have a rule that makes the first subtype include E and F -- anything
else would make the feature very hard to use. But what to do with (2)
through (4)?

Tucker's rule (Impose the same constraint unless the parent subtype'Last =
parent_subtype'Base'Last) provides the following answers: (2) A .. C; (3) B
.. F; (4) A .. F. I think (3) is unacceptable; (4) I dislike, but it
probably could be gotten used to.

I had suggested only expanding if the name denotes the first subtype. Then
all of the (2) .. (4) include only their original range.

However, *that* is very counter-intuitive: because there are additional
literals declared, but you can't use them without resorting to 'Base. Thus,
I find it crazy to inherit any constraint here; but for other uses (i.e.
subtypes of primitive subprograms), we would inherit everything.

Tucker suggested an alternative of disallowing deriving from a subtype. I
would be happy to adopt such a rule, but it clearly would cause a contract
model violation:

    generic
       type New_Alpha is new Alpha;
    package Fooey is
       type More_Alpha is new New_Alpha with (X, Y, Z);
    end Fooey;

    package Inst is new Fooey (New3);

One could adopt an assume-the-best/assume-the-worst rule to deal with this
(which would certainly require disallowing extensions in the body). But it
would be ugly.

> > ...
> > How do you assign the position numbers of the literals in the body? And
in
> > both cases, the position numbers of the literals aren't static (using my
> > "generic code sharing" glasses). Best to disallow these.
>
> Yes, I suppose we could disallow extension of a formal enumeration
> type in the body, in the same way we disallow extension of tagged
> types in a body.

I don't think we have a choice; there are too many problems otherwise
(meaning of 'Value, staticness of literals, the above problem, etc.)

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

From: Michael Yoder
Sent: Wednesday, February 21, 2001 12:55 PM


The more I think about it, the more I dislike a rule that checks for
matching of 'Last.  It would be like the rule that permits tweaking the
bounds of fixed point types, usually when they are powers of 2--a clever
idea that happens to work poorly in practice.  I'd prefer either of these
two (I'm open to other possibilities of course):

(1) A constraint is always inherited, so you must say "is new T'Base" to
avoid getting a constraint imposed that excludes the new values.  Some
(most?) will find this counterintuitive.

(2) A constraint is inherited unless the parent subtype denotes a first
named subtype.  You can, of course, use T'Base in any case, and then there
is no constraint to inherit.  It is possible that some (most?) will find
this even more counterintuitive, but I think it would cause fewer errors
overall.

My personal preference would, truthfully, be a third one: to undo the
decision that makes first named subtypes of enumeration types be
constrained.  However that's a preference expressed before exploring the
ramifications of such a decision.  :-)

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

From: Baird, Steve
Sent: Sunday, February 18, 2001 6:18 PM

From: Randy Brukardt, replying to Steve Baird
Sent: 2/15/2001 8:49 PM

> >   5) 3.4(9) and 13.1(5) would obviously require modification.
> >      Consider, for example, the attributes LAST and SIZE.

> 3.4(9): of course. 13.1(5): I think you meant 13.1(15).

Right. Sorry about any confusion.

--------

From: Randy Brukardt, replying to Steve Baird
Sent: 2/15/2001 8:49 PM

> >   6) This example should clearly be rejected,
> >
> >         type E1 is (Aa, Bb, Cc);
> >         generic
> >              type E2 is new E1;
> >         package G is
> >             Static_Value : constant := E2'Pos (E2'Base'Last);
> >         end G;
> >         type E3 is new E1 with (Dd, Ee);
> >         package I is new G (E2 => E3);
> >
> >    but on what basis? Does the definition of "static scalar
> >    subtype" need to be changed (i.e. treat a formal derived
> >    scalar type the same as a formal scalar type in 4.9(26)), or
> >    should the matching rules of 12.5.1(7-10) be tightened up?

> I don't get it. What's the problem here?
> I.Static_Value is clearly 5 in this case.

It is not ok if an expression in a generic is static and the
corresponding expression in some instance has a different static
value.

This would lead to problems.

Suppose, for example that you have something like

    generic
        ...
    package G is
        ...
        Static_Value : constant := ... ;
    end G;

    package body G is
        type T is array (Boolean) of Integer;

        X : T := (False => ..., (Static_Value = 3) => ...);
        Y : Integer := ... ;
    begin
        case Y is
            when Integer'First .. 2 | 4 .. Integer'Last => ... ;
            when Static_Value => ... ;
        end case;
    end G;

This will compile successfully as long as G.Static_Value = 3.
But what does this mean if you have some instantiation I, of G, such
that I.Static_Value = 5?

If "I.Static_Value is clearly 5" in my original example
(or in a modified version where
    type E2 is new E1;
is replaced with
    type E2 is new E1'Base;
) then it seems like there is a problem.

Making all scalar formal types non-static, rather than just
"formal scalar types", would also solve some similar problems that
have nothing to do with enumeration type extension. Perhaps that
warrants a separate AI.

--------

From: Randy Brukardt, replying to Steve Baird
Sent: 2/16/2001 10:38 PM

> However, there is a (minor) problem if someone actually uses 'Base:
>
>  package Pkg1 is
>	... as before...
>      procedure Proc (X : E1'Base);
>  end Pkg1;
>  package Pkg2 is
>      type E2 is new Pkg1.E1 with (Dd, Ee);
>  end Pkg2;
>
> In this case, Proc would indeed get all of the literals, and that
> might be surprising. I claim that if someone actually uses the
> unconstrained type, they must have wanted anything possible, and thus
> we still don't need to change the rule.

A call to Pkg2.Proc passing in an extension value (e.g. Pkg2.Dd) will
raise Constraint_Error.

3.4(27): "the normal conversion of each actual parameter to the subtype
of the corresponding formal parameter ... performs any necessary type
conversion as well".

Given this, it seems a bit odd that the parameter subtype would include
the extension values.

On the other hand, I don't see that it introduces any language definition
problems (although implementing it would be annoying - it means that
checking an actual parameter against the subtype of the formal is not
enough and additional constraint checking is needed).

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

From: Randy Brukardt
Sent: Monday, February 19, 2001 2:31 PM

Steve Baird said:

> Making all scalar formal types non-static, rather than just
> "formal scalar types", would also solve some similar problems that
> have nothing to do with enumeration type extension. Perhaps that
> warrants a separate AI.

I tend to agree (although I couldn't think of an example off-hand). Do you
have an example of a problem that occurs without enumeration type extension?
(If you do, then we probably do need an AI on this, as it would imply a bug
in the language.)

[Editor's note: This thread continues in AI-00263.]

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

From: Michael Yoder
Sent: Friday, February 23, 2001 11:26 AM

The "check 'Last" rule seems to lead to an unpleasant consequence.  Consider:

      type E is (Red, Blue, Green);
      function Returns_Red   return E is separate; --  always returns Red
      function Returns_Green return E is separate; --  always returns Green
      subtype S1 is E range Returns_Red .. Returns_Green;
      subtype S2 is E range Red .. Green;

      type E1 is new S1 with (Mauve);
      type E2 is new S2 with (Taupe);

The rule must, presumably, demand that the constraint be static *and* that
'Last match.  So E2 would include a new value and E1 wouldn't, even though
their bounds were identical at run time.  Of course, this sort of behavior
would occur for the "first named subtype" rule, but it seems less
disturbing because the rule is frank about making a special case.

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




Questions? Ask the ACAA Technical Agent