Version 1.4 of ais/ai-00438.txt

Unformatted version of ais/ai-00438.txt version 1.4
Other versions for file ais/ai-00438.txt

!standard 11.4.1(02)          05-11-22 AI95-00438/02
!standard 11.4.1(06)
!standard 11.4.1(16)
!class binding interpretation 05-10-21
!status Amendment 200Y 05-10-21
!status WG9 Approved 06-06-09
!status ARG Approved 8-0-3 05-11-19
!status work item 05-10-21
!status received 05-10-17
!priority Medium
!difficulty Easy
!subject Stream attribute availability for limited language-defined types
!summary
The language-defined limited type Ada.Exceptions.Exception_Occurrence has available stream attributes.
!question
Per AI-195, a stream attribute of a limited type is available at a place only if it was defined by an attribute definition clause that is visible at the place.
The visible part of the declaration of Ada.Exceptions as specified by 11.4.1(2)..11.4.1(7) does not contain the declaration of suitable subprograms nor the appropriate attribute_definition_clause. Only the private part is left unspecified. The language of 11.4.1(16) sets forth requirements for the implementation of these attributes, but they do not appear to be available. Should they be available? (Yes.)
!recommendation
(See summary.)
!wording
[Note: This wording assumes AI-441 is approved.]
Add the following to the end of the visible part Ada.Exceptions:
procedure Read_Exception_Occurrence
(Stream : not null access Ada.Streams.Root_Stream_Type'Class;
Item : out Exception_Occurrence);
procedure Write_Exception_Occurrence
(Stream : not null access Ada.Streams.Root_Stream_Type'Class;
Item : in Exception_Occurrence);
for Exception_Occurrence'Read use Read_Exception_Occurrence; for Exception_Occurrence'Write use Write_Exception_Occurrence;
Add "with Ada.Streams;" to the start of this package.
Replace 11.4.1(16) by (moving this to Static Semantics):
Write_Exception_Occurrence writes a representation of an exception occurrence to a stream; Read_Exception_Occurrence reconstructs an exception occurrence from a stream (including one written in a different partition).
!discussion
11.4.1(16) discusses the implementation of these attributes, so there is a clear intent that they actually can be called.
We checked all of the language-defined limited types to see if they should support streaming:
Directory_Entry_Type (A.16): no, what would it mean? Exception_Occurrence (11.4.1): yes, should stream. File_Type (A.8, others): no, surely you don't want to stream the contents. Generator (A.5.2): this could be streamed, but the package provides type
State as a nonlimited version of a Generator, so there isn't much need.
Group_Budget (D.14.2): no, probably a runtime object anyway. Limited_Controlled (7.6): no, does not need to stream. Root_Stream_Type (13.13.1): no. streaming a stream? Hopefully not. Search_Type (A.16): no, a temporary thing anyway. Suspension_Object (D.10): no, again, a runtime object. Timer (D.14.1): no, again, a runtime object. Timing_Event (D.15): no, again, a runtime object.
Limited_Controlled might need some explanation. While the package Ada.Finalization is a remote types package, only types that have available attributes need to be streamed. This allows users to create remote types derived from Limited_Controlled without Limited_Controlled itself being externally streamable. That's important, because making Limited_Controlled externally streamable would be a huge incompatibility: any type derived from it that has "really limited" components (such as a task or protected component) would need to have stream attributes defined.
For Exception_Occurrence, we've declared procedures and the stream attributes explicitly in the visible part. We considered three options for handling this:
(1) A textual rule saying that the attributes are available. That would
require some form of compiler magic to avoid name pollution (this could not be expressed in pure Ada).
(2) Declaring the subprograms and attribute specifications at the end of
the package. The subprograms in this case would be primitive and thus would be inherited on derivation. That could cause various side-effects. In addition, there is a use clause incompatibility, in that the new names might conflict with existing user names.
(3) Declaring the subprograms in a nested package, followed by the attribute
specifications at the end of the package. We still have the use clause incompatibility, but it is less likely because there is only one name made visible.
We chose the second option, as it isn't as gimmicky as (3), and doesn't require compiler magic like (1). We prefer to avoid magic as Ada users often use these package specifications as examples of how to write Ada packages, and it is preferable that they can actually write similar packages themselves.
The incompatibilities of (2) are not considered to be a major issue. The names of the procedures are specific enough that they're unlikely to occur in user code. We could have eliminated the incompatibility completely by giving the routines names that included some non-Latin-1 characters, but that is even more gimmicky that (3), so it was also rejected.
!corrigendum 11.4.1(02)
Replace the paragraph:
package Ada.Exceptions is type Exception_Id is private; Null_Id : constant Exception_Id; function Exception_Name(Id : Exception_Id) return String;
by:
with Ada.Streams; package Ada.Exceptions is type Exception_Id is private; Null_Id : constant Exception_Id; function Exception_Name(Id : Exception_Id) return String;
!corrigendum 11.4.1(06)
Replace the paragraph:
procedure Save_Occurrence(Target : out Exception_Occurrence; Source : in Exception_Occurrence); function Save_Occurrence(Source : Exception_Occurrence) return Exception_Occurrence_Access; private ... -- not specified by the language end Ada.Exceptions;
by:
procedure Save_Occurrence(Target : out Exception_Occurrence; Source : in Exception_Occurrence); function Save_Occurrence(Source : Exception_Occurrence) return Exception_Occurrence_Access;
procedure Read_Exception_Occurrence (Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Exception_Occurrence); procedure Write_Exception_Occurrence (Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : in Exception_Occurrence);
for Exception_Occurrence'Read use Read_Exception_Occurrence; for Exception_Occurrence'Write use Write_Exception_Occurrence;
private ... -- not specified by the language end Ada.Exceptions;
!corrigendum 11.4.1(16)
Replace the paragraph:
Implementation Requirements
The implementation of the Write attribute (see 13.13.2) of Exception_Occurrence shall support writing a representation of an exception occurrence to a stream; the implementation of the Read attribute of Exception_Occurrence shall support reconstructing an exception occurrence from a stream (including one written in a different partition).
by:
Write_Exception_Occurrence writes a representation of an exception occurrence to a stream; Read_Exception_Occurrence reconstructs an exception occurrence from a stream (including one written in a different partition).
!ACATS test
ACATS C-Tests should be constructed to check that Ada.Exceptions.Exception_Occurrence can be streamed. (Indeed, it would be useful to check most of the non-limited types as well.)
!appendix

From: Thomas Quinot
Sent: Friday, April 22, 2005  4:05 AM

Per AI-195, a stream attribute of a limited type is available at a place
only if it was defined by an attribute definition clause that is visible
at the place.

We know of a number of cases where users assume that
Exception_Occurrence'{Read,Write} are available for application code,
and this seems to be a reasonable expectation because of the language of
11.4.1(16), which sets forth requirements for the implementation of these
attributes.

Unfortunately, the visible part of the declaration of Ada.Exceptions
as specified by 11.4.1(2)..11.4.1(7) does not contain the declaration of
suitable subprograms nor the appropriate attribute_definition_clause.
Only the private part is left unspecified.

This looks like a hole in the specification.

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

From: Pascal Leroy
Sent: Friday, April 22, 2005  4:29 AM

> Per AI-195, a stream attribute of a limited type is available
> at a place only if it was defined by an attribute definition
> clause that is visible at the place.
>
> We know of a number of cases where users assume that
> Exception_Occurrence'{Read,Write} are available for
> application code, and this seems to be a reasonable
> expectation because of the language of 11.4.1(16), which sets
> forth requirements for the implementation of these attributes.

That's interesting.  Note that AI 195 is not really the culprit, and that
the bug has been there from day 1, since 13.13.2(36) says that a reference
to Write or Read is illegal for a limited type.

> Unfortunately, the visible part of the declaration of
> Ada.Exceptions as specified by 11.4.1(2)..11.4.1(7) does not
> contain the declaration of suitable subprograms nor the
> appropriate attribute_definition_clause. Only the private
> part is left unspecified.

I agree that we want to show attribute_definition_clauses for Read and
Write in the specification of Ada.Exceptions, and that maybe we want a
comforting sentence saying that these attributes are available in the
visible part.  I don't think we want to pollute the specification with the
"suitable subprograms", though.  Implementers will have to do some magic
there.

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

From: Bob Duff
Sent: Friday, April 22, 2005  7:00 AM

I agree, except: why require magic?  Why not just declare these in the
normal way (picking names that are unlikely to conflict with existing
user-defined names)?

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

From: Thomas Quinot
Sent: Friday, April 22, 2005  7:20 AM

A drawback of such a non-magic solution is that it forces
Ada.Exceptions to always depend upon Ada.Streams.

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

From: Tucker Taft
Sent: Friday, April 22, 2005  9:28 AM

We might want to put them in a subpackage, so they don't become
primitive operations of the Exception_Occurrence type.

E.g.:

    package Ada.Exceptions is
       ...
       type Exception_Occurrence is ...

       package Exception_Occurrence_Streams is
           procedure Read(...
           procedure Write(...
       end Exception_Occurrence_Streams;

       for Exception_Occurrence'Read
         use Exception_Occurrence_Streams.Read;

       ...

   end Ada.Exceptions;

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

From: Randy Brukardt
Sent: Thursday, October 20, 2005  6:21 PM

Pascal Leroy wrote replying to me:
>> This [the e-mail listed above] seems like a candidate for AC-111
>> (corrections). But I don't know what the correction actually is.
>> Tucker suggested
>> a nested package:
>
>This is messy because RM95 and RM95+TC11 are very much unclear.  Because
>this type is limited, its stream attributes can only be used if they have
>been specified, but the visible part of Ada.Exceptions doesn't show an
>attribute definition clause, and the private part is
>implementation-defined.  The only hint we have is 11.4.1(16), but that's
>not normative.  So I'd say that strictly speaking streaming this type
>ought to have been illegal in Ada 95 (I know that our customers do stream
>exception occurrences, so obviously we are not abiding by the letter of
>the Book).

I agree. In any case, AI-195 is a BI, so it applies to RM95. And as a BI, it
says that there was never any intent that you had to look in the private part.

>I have a serious concern here, because this problem can possibly exist for
>each and every predefined limited type in the language.  Maybe some
>implementation has defined Ada.Numerics.Float_Random.Generator'Write in
>the private part of Ada.Numerics.Float_Random, and suddenly the
>availability rules are going to break customer code.  Nasty.

Tough. Looking into the private part is always bad taste, if not wrong.

>If we want to solve this problem, we should do it globally rather than
>focus on Ada.Exceptions.

Now you're treading on very thin ice. For Ada.Exceptions, there is clear
evidence that the designers wanted it to be streamable (in the form of
11.4.1(16)). No such evidence exists for any other type (well, other than the
ARG attaching "Remote_Types" to Ada.Finalization, which is an after-the-fact
judgment. Note that the pragma is illegal on Ada.Finalization, given the rules
in E.2.2 - that seems in bad taste to me).

> Here are the predefined limited types that I
> found (I am not entirely sure that I didn't miss some), with an indication
> of my feeling as to whether or not they need to stream.

>- Directory_Entry_Type: no, what would it mean?
>- Exception_Occurrence: should stream.
>- File_Type: no, surely you don't want to stream the contents.
>- Generator: a pure value, should stream.
>- Group_Budget: no, probably a runtime object anyway.
>- Limited_Controlled: part of a remote types package, better stream.
>- Root_Stream_Type: streaming a stream?  Hopefully not.
>- Search_Type: no, a temporary thing anyway.
>- Suspension_Object: no, again, a runtime object.
>- Timer: no, ditto.
>- Timing_Event: no, ditto.

>At any rate, I certainly don't want to pollute existing predefined
>packages with nested packages and other junk, so I think we should just
>say something like:
>
>"The attributes Read, Write, Input and Output for T are available at the
>end of the visible part of P."

And now you've fallen through the ice.

>and let the implementers figure out how to do this using some form or
>magic.  (The only magic is to create stream subprograms that don't pollute
>the namespace; it's always OK to add a with Ada.Streams if you need one
>because it has no visible effect; note in particular that Ada.Streams is
>pure so there are no nasty categorization effects.)

But that magic is incredibly disruptive. It affects *everything*, and it has
absolutely no user benefit.

Now, if the only magic was a pragma (this might give you a case of deja-vu):

         pragma Streaming_Available (Generator);

I'd put up with it. But the default attributes for Generator are not going to
work in any implementation, as Generator has to either be Rosen-trick type, or
a limited controlled type with heap pointer. So you definitely do need
first-class subprograms to do the trick.

Pragmatically, both Ada.Finalization.Controlled and Ada.Exceptions are
completely magic in our compiler anyway. So the effort is the same no matter
what the rules are, because the symboltable is created by hand with a bunch of
aggregates that have the right effect. So I wouldn't object too strongly to
magic in those cases (although I would expect other implementers to do so).

But I *would* object very strongly to requiring magic for
Ada.Numerics.Float_Random, etc. Especially as this would be the *only* use of
such magic; you're talking a weeks work for a feature which is never going to
be used. Most likely, implementers would totally ignore the requirement, which
is hardly of any benefit to users.

Honestly, I see no problem with making the routines explicit. Their profiles
are unlikely to conflict with anything in user code, and the names can make
that even more unlikely too (I'd suggest Stream_Read and Stream_Write or
something like that -- "Read" and "Write" are too likely to conflict). As
procedures, there is no problem with inheritance (it will just work) - and that
presumes someone is deriving from these types in the first place (only likely
for Limited_Controlled).

Yes, it will clutter the packages in question, but so what?

So, either limit this to "magic" packages where there is clear requirements for
streaming (Limited_Controlled and Exceptions), or forget the magic.

(And "magic" means that the spec of Ada.Finalization is technically illegal for
pragma Remote_Types. I think we ought to provide better examples than that to
users; people do use the predefined stuff as examples of Ada style.)

Note that both Bob and Tuck were against magic in the original exchange. Only
you seemed in favor of it, so I think you might be on an island there. Probably
we need to ask the ARG to know for sure.

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

From: Pascal Leroy
Sent: Friday, October 21, 2005  3:46 AM

> Tough. Looking into the private part is always bad taste, if
> not wrong.

I couldn't agree more.

> >If we want to solve this problem, we should do it globally
> rather than
> >focus on Ada.Exceptions.
>
> Now you're treading on very thin ice.

I treading on perfectly firm ground when I say that we should at least
take a look at end and every predefined limited type and make a decision
as to whether or not it should stream.  If we don't make
Limited_Controlled streamable, we have plenty of egg on our face.

> >At any rate, I certainly don't want to pollute existing predefined
> >packages with nested packages and other junk, so I think we
> should just
> >say something like:
> >
> >"The attributes Read, Write, Input and Output for T are available at
> >the end of the visible part of P."
>
> But that magic is incredibly disruptive. It affects
> *everything*, and it has absolutely no user benefit.

For a library-based compiler, it doesn't seem that hard to me: just
implement a switch that says to the lexer "please allow consecutive
underscores" and write your source as follows:

	procedure Stream__Read ... -- Note the two underscores.
	for T'Read use Stream__Read...

Compile this source with the switch, and ship the resulting library to
your customers: it won't create name clashes, and I am ready to bet that
all the rest of you compiler will happily deal with the name Stream__Read.

For a source-based compiler where you keep recompiling the same source
over and over, this is not so simple.  But anyway, implementation
arguments are not exactly relevant here.

> Now, if the only magic was a pragma (this might give you a
> case of deja-vu):
>
>          pragma Streaming_Available (Generator);
>
> I'd put up with it. But the default attributes for Generator
> are not going to work in any implementation, as Generator has
> to either be Rosen-trick type, or a limited controlled type
> with heap pointer.

In our implementation Generator is a simple array wrapped in a limited
record.  The limitedness ensures that it is passed by reference, and then
it is updated using 'Address.  Not sure if there are implementations that
actually use the Rosen trick: that would seem likely to confuse optimizers
if you ask me.  As for a controlled implementation, that is certainly an
option, but it would be awfully expensive.

> Pragmatically, both Ada.Finalization.Controlled and
> Ada.Exceptions are completely magic in our compiler anyway.

They are not particularly magic for us, but then we have a trick to create
identifiers with * in them, and that would take care of the name space
pollution.

> Honestly, I see no problem with making the routines explicit.
> Their profiles are unlikely to conflict with anything in user
> code, and the names can make that even more unlikely too (I'd
> suggest Stream_Read and Stream_Write or something like that
> -- "Read" and "Write" are too likely to conflict). As
> procedures, there is no problem with inheritance (it will
> just work) - and that presumes someone is deriving from these
> types in the first place (only likely for Limited_Controlled).

In my view, inheritance *is* a problem.  If any (limited) controlled type
gets a Stream_Read, there is the potential bug that someone will call it,
or that it will have unpleasant interferences with overriding_indicators,
etc.  So if you don't want magic, I definitely want a nested package.  The
downside of having a name that could conflict with use clauses is nothing
compared with the nuisance of inheriting these procedures.

So in that case I would go for:

	package Stream_Attributes is
	   procedure Read ...;
	   procedure Write ...;
	end Stream_Attributes;

	for T'Read use Stream_Attributes'Read;
	for T'Write use Stream_Attributes'Write;

This should go at the very end of the visible part.

> Note that both Bob and Tuck were against magic in the
> original exchange. Only you seemed in favor of it, so I think
> you might be on an island there. Probably we need to ask the
> ARG to know for sure.

You're right, I am isolated, so I suggest adding the nested packages.  If
you wish to ask the ARG, feel free to do so.

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

From: Randy Brukardt
Sent: Friday, October 21, 2005  9:13 PM

I was writing up the AI for this when I was struck by a problem.

Inheritance *is* a problem, regardless of how we write, because "availability"
is inherited for tagged types.

There's no problem for Exceptions.Exception_Occurrence or
Float_Random.Generator (they're not tagged).

But for Limited_Controlled, this means that *every* descendant has available
stream attributes. And having available stream attributes means that they
*must* be defined if in fact any of the components do not have available stream
attributes.

That's of course necessary so that calling Limited_Controlled'Class'Read will
work.

But that's a horrible incompatibility: every limited type derived from
Limited_Controlled (and I'm sure there is a lot of them in existing code) would
need stream attributes if the extension components contained anything "really
limited". Whether or not the program ever does any streaming. (While it would
be good for programmers to use streaming more, being forced to do it is not
likely to win any friends...)

This leaves us with some very bad choices, since Remote_Types means that
Limited_Controlled must be "externally streamable".

(1) Give up on OOP for streaming of limited types. (After everything we've done
to make limited and nonlimited equivalent, this would be horrible. Plus the
loss to OOP.)
(2) Give up on Ada.Finalization being a remote types package.
(3) Magic. Define Limited_Controlled to be externally streamable, but *not*
have available attributes. (That is, no attributes would be defined in the
spec, thus they are not available.) A remote type derived from it would have to
define attributes to make them available. Limited_Controlled itself couldn't be
streamed directly, but the DS annex could (they're empty types, so its not an
issue.)
(4) Magic 2. Leave things as they are. Add an AARM note that Limited_Controlled
is not externally streamable, so transporting it won't work - but doing so is
useless anyway, so no one will care. But it can be used as the basis for types
that are remote types. DS implementations would have to do *something* if it is
encountered, but we don't care what.
(5) ???

Any ideas?

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

From: Tucker Taft
Sent: Friday, October 21, 2005  9:13 PM

Limited_Controlled is limited.  Why would I expect
to be able to stream it?  I must have missed something
here, since it seems clear to me that we never expected
language-defined limited types to be streamable (with the one
"exception" of Exception_Occurrence, of course ;-).

Ahhh! I see the problem.  Our requirement on Remote_Types
that all visible types support external streaming should
have said that all visible types *with any available stream
attributes* shall support external streaming.  The same
should go for Pure packages.

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


Questions? Ask the ACAA Technical Agent