Version 1.5 of ais/ai-00264.txt

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

!standard 11.01 (02)          01-05-09 AI95-00264/01
!standard 11.03 (02)
!class amendment 01-05-09
!status No Action (10-0-0) 03-10-03
!status work item 01-05-09
!status received 01-05-09
!priority Medium
!difficulty Hard
!subject Exceptions as Types
!summary
Exception types are added to the language, in order to provide the ability to pass more information along with exceptions, and to provide a hierarchy of exceptions.
!problem
Ada 83 had a very basic exception mechanism. It was not possible to pass information when raising an exception, and exceptions were not integrated with the overall type structure of the language, making it impossible to pass exception-related information to subprograms, or to store it in data structures, etc.
Ada 95 added slightly more powerful capabilities in this area, with the facilities provided by the predefined package Ada.Exceptions. However, the new mechanism is still only intended for very basic error detection and logging, and doesn't provide a general-purpose communication mechanism. It has the merit of clarifying the notions of exception identity and exception occurrence, but exceptions are still not integrated with the typing model like protected objects or tasks are.
It is in fact possible to use the Message associated with an exception occurrence to pass (relatively small) objects when raising an exception, but this has to be done by marshalling the object into the string. While adequate for some purposes, this approach is unnecessarily contrived.
This situation is very unfortunate, because other modern programming languages like C++ or Java, which are in other ways inferior to Ada, have a much more powerful exception mechanism.
!proposal
Exception Types
A new class of types, called exception types, is added to the language. Exception types are untagged, composite, limited types (much like task types). There is no such thing as a formal exception type, so an exception type may only be passed to a generic formal limited private type or a formal derived type whose ancestor is an exception type.
A root exception type may be declared by a new form of type declaration:
type User_Defined_Exception is exception C1 : T1; C2 : T2; end exception;
A derived exception type may then be declared by extending the above type:
type Extended_Exception is new User_Defined_Exception with exception C3 : T3; C4 : T4; end exception;
A conventional exception declaration like:
Ada95_Exception : exception;
is then taken, for compatibility, to declare a root exception type with a single component Message of type String:
type Ada95_Exception (Length : Natural) is exception Message : String (1 .. Length); end exception;
An exception type may be used to complete a limited private type.
Raising An Exception
Raising an exception creates an exception occurrence (it is also possible to create exception occurrences by declaring objects of exception types, but this is not too interesting as exception types are limited).
For compatibility, an exception declared by a conventional exception declaration may be raised by a conventional raise statement:
raise Ada95_Exception;
On the other hand, an exception declared by an exception type declaration must be raised by a new form of raise statement, with specifies values for all the components of the exception occurrence:
raise Ada95_Exception'
(Length => 3,
Message => "foo");
This is the only context where an aggregate for an exception type is legal (aggregates may not normally be of a limited type). Note that extension aggregates are not usable in this context because exception types are not tagged.
Note that the Ada 95 routine Ada.Exceptions.Raise_Exception is equivalent to the above example.
Exception Handling
An exception handler like:
when E : User_Defined_Exception => ...
catches any exception occurrence of type User_Defined_Exception or of a type derived thereof. In that respect, the name of an exception type in a handler is somewhat similar to the name of a class-wide type: it covers an entire class of types.
All the handlers in a given block must have non-overlapping exception types. For example, the following is illegal:
exception
when User_Defined_Exception => when Extended_Exception =>
This rule is different from what Java does, but it seems more consistent with other rules of Ada (e.g., for case statements or conventional exception handlers). It ensures that which alternative is selected doesn't depend on the lexical order of the handlers.
Inside a handler, the choice parameter provides a constant view of the exception occurrence being handled. Component selection can be used to extract information from the occurrence:
exception
when E : User_Defined_Exception =>
if Func (E.T1) = Func (E.T2) then ...
(For compatibility, if the exception was declared by a conventional exception declaration, we will stick to the Ada 95 semantics, i.e., E is of type Ada.Exception.Exception_Occurrence.)
[Editor's note: What is the type of the exception is different for members of a list? That is:
when E : User_Defined_Exception | Constraint_Error =>
Probably this will need to be disallowed.]
An others choice may be used to cover all the exception types not covered by other handlers, but in such a handler the only possible usages of the choice parameter are those provided by Ada 95.
!discussion
What If We Did It Like Java?
Assume for a moment that we wanted to mirror in Ada the exception mechanism provided by Java (bear in mind that this is only an analogy, not the real mechanism proposed by this amendment). One way to do it would be to posit an anonymous type from which all exceptions are derived:
type exception is abstract tagged limited private;
From this type, exceptions would be declared as type extensions. So any good old Ada 95 exception like:
Ada95_Exception : exception;
would be declared by:
type Ada95_Exception is new exception with record Message : String (…); end record;
Note that the component Message reflects the fact that every exception occurrence in Ada 95 may be parameterized by a string.
Following the same model, we would be able to declare exception parameterized with non-String data:
type User_Defined_Exception is new exception with record C1 : T1; C2 : T2; end record;
or extend existing exceptions to associate more data with an exception occurrence:
type Extended_Exception is new User_Defined_Exception with record C3 : T3; C4 : T4; end record;
With this analogy, the exception identity corresponds to the tag of the type, and an exception occurrence is more-or-less an object of type exception'Class.
There are a number of reasons why we don't want to use tagged types to represent exceptions, though. First, some of the rules related to tagged types would cause inacceptable limitations; for instance, it would not be possible to declare an exception in a nested scope, because that would violate the accessibility rule of RM95 3.9.1(3). Second, existing implementations might use for exceptions a representation totally different from that of tagged types; we don't want to force these implementations to change. Finally, many of the capabilities of tagged types, like dispatching calls or membership tests, are irrelevant in this context.
Principles
Still, the above analogy has the merit of showing a number of underlying principles:
- An exception declares a type, which is characterized by its exception
identity.
- From that type, exception occurrences may be created, typically by a raise
statement.
The proposal has uses these principles.
Methodological Note
The reason why an exception type name in a handler covers an entire class of types is methodological: we want to simplify exception handling by clients. A service package should be able to use fine-grained exceptions to signal anomalous conditions using a very precise parameterization. But some clients might not be interested in the details, so they might want to handle an entire class of exceptions in one fell swoop. Say that E0 is a root exception type and E1, E2 and E3 are derived exception types. The service package would contain code like:
if ... then
raise E1'(...);
else
case ... is
when ... => raise E1'(...); when ... => raise E2'(...); when ... => raise E3'(...);
end case;
end if;
then a client that wants to know the details of the failures would have handlers like:
exception
when E1 => when E2 | E3 =>
but a client that is only interested to know that "something went wrong" in the service package would have a handler like:
exception
when E0 => ...
A further benefit is that this second client would presumably still work correctly if a new derived exception type E4 is added during maintenance.
Implementation Issues
While this proposal introduces some amount of implementation complexity, it doesn't seem insuperably complicated:
- Syntax. Assuming that the new syntax doesn't make the grammar hopelessly
ambiguous, this part should not be problematic.
- Static semantics. Exception types are a new concept, and presumably there
would be a number of legality and static semantics rules associated with them. However, one area where they have very little impact is generics, because we are not adding new formal types. In general exception types are very similar to protected or task types, so one would expect that they could be handled similarly by compilers.
- Dynamic semantics. I can see two potentially problematic areas here:
o Exception types can be unconstrained types, and therefore the
occurrences may have an arbitrarily large size. In Ada 95, the Message string could be limited to 200 characters, so this might put an additional burden on the implementations. On the other hand, this is somewhat mitigated by the fact that exception occurrences are limited, so they cannot be resized at the drop of a hat.
o Exception types may have controlled subcomponents. From a language
design standpoint, this requires to define precisely when occurrences are finalized. From an implementation standpoint, this is another interaction between finalization and the rest of the language. Undoubtedly, if this proposal is deemed worth pursuing, there is still a lot of language design to be done to sort out the interactions with other language features and the compatibility issues, among others.
!example
[Editor's note: We need a good example here.]
!ACATS test
!appendix

From: Pascal Leroy
Sent: Friday, May 4, 2001 4:34 AM

Purpose

Ada 83 had a very basic exception mechanism. It was not possible to pass
information when raising an exception, and exceptions were not integrated with the
overall type structure of the language, making it impossible to pass exception-related
information to subprograms, or to store it in data structures, etc.

Ada 95 added slightly more powerful capabilities in this area, with the facilities
provided by the predefined package Ada.Exceptions. However, the new mechanism
is still only intended for very basic error detection and logging, and doesn't provide a
general-purpose communication mechanism. It has the merit of clarifying the notions
of exception identity and exception occurrence, but exceptions are still not integrated
with the typing model like protected objects or tasks are.

It is in fact possible to use the Message associated with an exception occurrence to
pass (relatively small) objects when raising an exception, but this has to be done by
marshalling the object into the string. While adequate for some purposes, this
approach is unnecessarily contrived.

This situation is very unfortunate, because other modern programming languages
like C++ or Java, which are in other ways inferior to Ada, have a much more
powerful exception mechanism.

This paper presents a proposal for extending Ada with new features related to
exceptions. These features are compatible with Ada 95, yet they provide roughly the
same expressive power as Java.


What If We Did It Like Java?

Assume for a moment that we wanted to mirror in Ada the exception mechanism
provided by Java (bear in mind that this is only an analogy, not the real mechanism
proposed by this paper). One way to do it would be to posit an anonymous type
from which all exceptions are derived:

type exception is abstract tagged limited private;

From this type, exceptions would be declared as type extensions. So any good old
Ada 95 exception like:

Ada95_Exception : exception;

would be declared by:

type Ada95_Exception is new exception with
	record
		Message : String (…);
	end record;

Note that the component Message reflects the fact that every exception occurrence in
Ada 95 may be parameterized by a string.

Following the same model, we would be able to declare exception parameterized
with non-String data:

type User_Defined_Exception is new exception with
	record
		C1 : T1;
		C2 : T2;
	end record;

or extend existing exceptions to associate more data with an exception occurrence:

type Extended_Exception is
	new User_Defined_Exception with
	record
		C3 : T3;
		C4 : T4;
	end record;

With this analogy, the exception identity corresponds to the tag of the type, and an
exception occurrence is more-or-less an object of type exception'Class.

There are a number of reasons why we don't want to use tagged types to represent
exceptions, though. First, some of the rules related to tagged types would cause
inacceptable limitations; for instance, it would not be possible to declare an exception
in a nested scope, because that would violate the accessibility rule of RM95 3.9.1(3).
Second, existing implementations might use for exceptions a representation totally
different from that of tagged types; we don't want to force these implementations to
change. Finally, many of the capabilities of tagged types, like dispatching calls or
membership tests, are irrelevant in this context.


A Proposal

Still, the above analogy has the merit of showing a number of underlying principles:
-	An exception declares a type, which is characterized by its exception identity.
-	From that type, exception occurrences may be created, typically by a raise
	statement.

I will now use these principles to present a proposal for extending the exception
model of Ada 95.

Exception Types

A new class of types, called exception types, is added to the language. Exception types
are untagged, composite, limited types (much like task types). There is no such thing
as a formal exception type, so an exception type may only be passed to a generic
formal limited private type or a formal derived type whose ancestor is an exception
type.

A root exception type may be declared by a new form of type declaration:

type User_Defined_Exception is
	exception
		C1 : T1;
		C2 : T2;
	end exception;

A derived exception type may then be declared by extending the above type:

type Extended_Exception is
	new User_Defined_Exception with
	exception
		C3 : T3;
		C4 : T4;
	end exception;

(I am not religious about the syntax; I just tried to make it similar to tagged types but
still sufficiently distinct that it doesn't become confusing.)

A conventional exception declaration like:

Ada95_Exception : exception;

is then taken, for compatibility, to declare a root exception type with a single
component Message of type String:

type Ada95_Exception (Length : Natural) is
	exception
		Message : String (1 .. Length);
	end exception;

An exception type may be used to complete a limited private type.

Raising An Exception

Raising an exception creates an exception occurrence (it is also possible to create
exception occurrences by declaring objects of exception types, but this is not too
interesting as exception types are limited).

For compatibility, an exception declared by a conventional exception declaration
may be raised by a conventional raise statement:

raise Ada95_Exception;

On the other hand, an exception declared by an exception type declaration must be
raised by a new form of raise statement, with specifies values for all the components
of the exception occurrence:

raise Ada95_Exception'
			(Length => 3,
			 Message => "foo");

This is the only context where an aggregate for an exception type is legal (aggregates
may not normally be of a limited type). Note that extension aggregates are not usable
in this context because exception types are not tagged.

Exception Handling

An exception handler like:

when E : User_Defined_Exception => …

catches any exception occurrence of type User_Defined_Exception or of a type derived
thereof. In that respect, the name of an exception type in a handler is somewhat
similar to the name of a class-wide type: it covers an entire class of types.

All the handlers in a given block must have non-overlapping exception types. For
example, the following is illegal:

exception
	when User_Defined_Exception =>
	when Extended_Exception =>

This rule is different from what Java does, but it seems more consistent with other
rules of Ada (e.g., for case statements or conventional exception handlers). It ensures
that which alternative is selected doesn't depend on the lexical order of the handlers.

Inside a handler, the choice parameter provides a constant view of the exception
occurrence being handled. Component selection can be used to extract information
from the occurrence:

exception
	when E : User_Defined_Exception =>
		if Func (E.T1) = Func (E.T2) then …

(For compatibility, if the exception was declared by a conventional exception
declaration, we will stick to the Ada 95 semantics, i.e., E is of type
Ada.Exception.Exception_Occurrence.)

An others choice may be used to cover all the exception types not covered by other
handlers, but in such a handler the only possible usages of the choice parameter are
those provided by Ada 95.

Methodological Note

The reason why an exception type name in a handler covers an entire class of types is
methodological: we want to simplify exception handling by clients. A service
package should be able to use fine-grained exceptions to signal anomalous
conditions using a very precise parameterization. But some clients might not be
interested in the details, so they might want to handle an entire class of exceptions in
one fell swoop. Say that E0 is a root exception type and E1, E2 and E3 are derived
exception types. The service package would contain code like:
if … then
	raise E1'(…);
else
	case … is
		when … => raise E1'(…);
		when … => raise E2'(…);
		when … => raise E3'(…);
	end case;
end if;

then a client that wants to know the details of the failures would have handlers like:

exception
	when E1 =>
	when E2 | E3 =>

but a client that is only interested to know that "something went wrong" in the
service package would have a handler like:

exception
	when E0 => …

A further benefit is that this second client would presumably still work correctly if a
new derived exception type E4 is added during maintenance.

Implementation Issues

While this proposal introduces some amount of implementation complexity, it
doesn't seem insuperably complicated:

-	Syntax. Assuming that the new syntax doesn't make the grammar hopelessly
	ambiguous, this part should not be problematic.
-	Static semantics. Exception types are a new concept, and presumably there
	would be a number of legality and static semantics rules associated with them.
	However, one area where they have very little impact is generics, because we
	are not adding new formal types. In general exception types are very similar to
	protected or task types, so one would expect that they could be handled
	similarly by compilers.
-	Dynamic semantics. I can see two potentially problematic areas here:
	o	Exception types can be unconstrained types, and therefore the occurrences
		may have an arbitrarily large size. In Ada 95, the Message string could be
		limited to 200 characters, so this might put an additional burden on the
		implementations. On the other hand, this is somewhat mitigated by the
		fact that exception occurrences are limited, so they cannot be resized at the
		drop of a hat.
	o	Exception types may have controlled subcomponents. From a language
		design standpoint, this requires to define precisely when occurrences are
		finalized. From an implementation standpoint, this is another interaction
		between finalization and the rest of the language.
		Undoubtedly, if this proposal is deemed worth pursuing, there is still a lot of
		language design to be done to sort out the interactions with other language features
		and the compatibility issues, among others.

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

From: Pascal Leroy
Sent: Thursday, May 10, 2001 3:08 AM

Btw, you have a good question about the type of E in:

    when E : User_Defined_Exception | Constraint_Error =>

and I didn't think of this case and I don't know what the right answer is.

> I realize this isn't a completely thought out proposal. Why do you not think
> that a generic formal parameter type should be added? It seems like a
> natural requirement (most other types have one), and it doesn't seem to add
> any problems that the proposal doesn't already have.

For the same reason that we don't have formal types for record types and
protected types.  Exception types are in fact very similar to protected types:
they are record-ish things, with some special operations (raising and handling).
We don't have formal parameters that expose the internals of records or
protected types, and for good reasons (it would be hard both from a language
design standpoint and for an implementation standpoint, not to mention the
impact on shared generics).  I believe there is no compelling reason to invent
an entirely new mechanism for exceptions.

> I think controlled
> components will be a killer. You may want to think about handling those,
> because the proposal has no chance without a good solution to that problem.

I actually gave quite a bit of thought to that issue.  First, note that
exception types have to be return-by-reference types, so there is never any
copy.  Therefore, the only issue is with initialization and finalization.

Initialization is easy, it happens when you raise the exception (we would need
to define what happens when Initialize raises an exception, but yawn, this is
not terribly exciting).

Finalization is harder.  But keep in mind that an exception occurrence is never
propagated across tasks.  So the mechanism I have in mind is: (1) when an
occurrence is handled and not reraised, it is finalized prior to exiting the
handler and (2) when an occurrence is not handled, it is finalized as part of
the finalization of the task that raised it.  If an exception is raised during a
rendezvous, two occurrences are created, one in each task, and they both live
their lives independently.  If a reraise statement is execute, it propagates the
same occurrence.

An exception occurrence could even have task or protected subcomponents, and
their finalization would work similarly (why anyone would want to do that is
beyond me, though).

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

From: Randy Brukardt
Sent: Thursday, May 10, 2001 2:26 PM

> and I didn't think of this case and I don't know what the
> right answer is.

The only answer I could think of is that this is illegal. Anything else seems
too confusing. It would only be illegal for exceptions using the new
capabilities, so there wouldn't be a compatibility problem.

> I actually gave quite a bit of thought to that issue.  First, note that
> exception types have to be return-by-reference types, so there is never any
> copy.  Therefore, the only issue is with initialization and finalization.

So procedure Ada.Exceptions.Save_Occurrence doesn't work on these guys? That
seems like a step backwards. (Assuming it doesn't, then it seems that none of
the other routines in Ada.Exceptions do, either, and that certainly is
unfriendly.)

It really appears that occurrences need to be tagged, because we need them to
be able to be classwide so that the routines in Ada.Exceptions can work. (Note
that does *not* mean that *exceptions* are tagged, only that *occurrences*
are.)

> Initialization is easy, it happens when you raise the exception (we would
> need to define what happens when Initialize raises an exception, but yawn,
> this is not terribly exciting).

We would have to be careful to apply AI-83 to the aggregate here, so that no
copy takes place.

> Finalization is harder.  But keep in mind that an exception occurrence is
> never propagated across tasks.

But it can be copied to another task. (Using Save_Occurrence).

> An exception occurrence could even have task or protected subcomponents, and
> their finalization would work similarly (why anyone would want to do that is
> beyond me, though).

But that implies that Save_Occurrence would no longer be usable, and that
certainly seems like a step backwards. Perhaps Save_Occurrence could be defined
as:

    procedure Save_Occurrence (Target : out Exception_Occurrence;
                               Source : in Exception_Occurrence'class);

with it only copying the "parent part" (in this case, the occurrence
information).

There is a problem, however, with Ada.Exceptions.Exception_Message, as the
definition in your proposal says that no message is available in most exception
types. I suppose it too would not be classwide. (Exception_Name and
Exception_Information ought to work on all exception occurrences).

Sigh. More work needed here.

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

From: Pascal Leroy
Sent: Friday, May 11, 2001 1:51 AM

> So procedure Ada.Exceptions.Save_Occurrence doesn't work on these guys? That
> seems like a step backwards. (Assuming it doesn't, then it seems that none
> of the other routines in Ada.Exceptions do, either, and that certainly is
> unfriendly.)

Well, evidently Save_Occurrence cannot work if one of the components of the
exception is, say, a task.

My idea was that Save_Occurrence (and most of the stuff in Ada.Exceptions)
would only be retained for Ada 95-like exceptions.  For new-style
exceptions, the copy operation would have to be implemented by the user,
either using component-by-component assignment or using streaming.  Either
way, we don't have to define what happens when you copy an entire exception
object.

> It really appears that occurrences need to be tagged, because we need them
> to be able to be classwide so that the routines in Ada.Exceptions can work.
> (Note that does *not* mean that *exceptions* are tagged, only that
> *occurrences* are.)

I am not sure how occurrences may be tagged while exceptions aren't.  If you
mean that the implementation must record the exception id as some hidden
component of the exception occurrence, then yes, you're right.

At any rate, most of the stuff in Ada.Exceptions is only needed because
Exception_Occurrence is a private type.  New-style exceptions are not
private, so you can just access their internals as you do with vanilla
records.

> There is a problem, however, with Ada.Exceptions.Exception_Message, as the
> definition in your proposal says that no message is available in most
> exception types. I suppose it too would not be classwide. (Exception_Name
> and Exception_Information ought to work on all exception occurrences).

I don't see why.  If you want to use exceptions for communication purposes
inside your program (as opposed to logging, reporting errors to the user,
etc.) a structured data type is probably much better than a string.  If you
actually want a message, put a string component in you exception type
definition.

In essence, I guess I am saying that I see Ada.Exceptions as mostly
obsolescent, and retained for compatibility with Ada 95.

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

From: Randy Brukardt
Sent: Friday, May 11, 2001 2:23 PM

> I am not sure how occurrences may be tagged while exceptions aren't.  If you
> mean that the implementation must record the exception id as some hidden
> component of the exception occurrence, then yes, you're right.

No, I mean that the type Exception_Occurrence is a tagged type. The "exception
type" is just a component of it.

> At any rate, most of the stuff in Ada.Exceptions is only needed because
> Exception_Occurrence is a private type.  New-style exceptions are not
> private, so you can just access their internals as you do with vanilla
> records.
>
I think we agree on Exception_Message. But Exception_Name and
Exception_Information are critical.

> In essence, I guess I am saying that I see Ada.Exceptions as mostly
> obsolescent, and retained for compatibility with Ada 95.

Not a chance. Ada.Exceptions is critical for logging and debugging purposes;
making it unusable with a substantial fraction of exceptions is unacceptable.
Calling it obsolescent is completely ridiculous.

For example, it would be nice if Claw had a root exception that parented all of
the exceptions it raises, so that you could just handle "any Claw error". But
callers of Claw certainly need to be able to log messages and report the
exception information.

All exception occurrences have an Exception_Name. This is useful any time that
there is more than one exception handled in a handler. Certainly, this proposal
*increases* the need for the exception name, because a parent exception can
cover many of them. (For debugging, knowing the exact exception is critical).

Similarly, many compilers put useful information into the occurrence, such as
line numbers (Janus/Ada puts an entire call traceback into it). That information
is very valuable for debugging, and it has nothing whatsoever to do with
anything that the user can create. Loss of access to it is unacceptable.

For instance, in your current proposal, if I added a root exception to Claw,
which then all of the other exceptions were derived from, that would make all of
the Claw exceptions "exception types". If I then wrote the (common) handler:

    when Info:Claw.Any_Claw_Error =>
	   Log_Item ("This routine - " & Ada.Exceptions.Exception_Information(Info));

you're saying that this is illegal?? No way. I will vote against any proposal
which restricts the use of Ada.Exceptions.Exception_Information or
Ada.Exceptions.Exception_Name. Period. They are far more important than any
possible gain from an exception hierarchy or carrying additional information
with exceptions.

----

What I was proposing was that type Exception_Occurrence be tagged, so that
Ada.Exceptions could be rewritten with appropriate classwide routines.

package Ada.Exceptions is

    ...
    function Exception_Identity (X : Exception_Occurrence'Class) return Exception_Id;
    function Exception_Name (X : Exception_Occurrence'Class) return String;
    function Exception_Information (X : Exception_Occurrence'Class) return String;
    function Exception_Message (X : Exception_Occurrence) return String; -- Not classwide; only works on original type.
    procedure Save_Occurrence (Target : out Exception_Occurrence; Source : in Exception_Occurrence);
end Ada.Exceptions;

However, that has an inheritance problem. Probably we would need to introduce
"Root_Exception_Occurrence", and make the global operations available on it.

Note that the tag of these types is not really relevant; it doesn't need to have
anything to do with the exception type, only that you can tell
"Exception_Occurrence" from an occurrence created from some exception type.

(I think it is a mistake to blur the distinction between an exception and an
occurrence; they're clearly different things.)

If I had time, I'd work out a complete alternate model, but I don't have time.
(And personally, I don't think this proposal is going any further than
extensible enumeration types.)

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

From: Pascal Leroy
Sent: Monday, May 14, 2001 4:30 AM

> No, I mean that the type Exception_Occurrence is a tagged type. The
> "exception type" is just a component of it.

I started along that line, and tried to piggyback on tagged types, but failed.
First, if you make Exception_Occurrence tagged, you have a gross incompatibility
(it isn't at the moment).  But more importantly, you don't want to require
implementations to change their representation of exception occurrences to make
them tagged.  That could be a major, major change for some implementations, and
I am trying to minimize the cost of the new feature (it's useless to design a
nice-and-fancy mechanism if nobody implements it).

> I think we agree on Exception_Message. But Exception_Name and
> Exception_Information are critical.

Agreed, but these could be replaced by attributes for new-style exceptions.
(Aside: I think a 'Name attribute would in general be a useful capability for
just about any entity of the language.  I have often seen code containing
logging messages like "exception foo in subprogram bar" and then Bar is renamed
into Baz and the message is left unchanged.  I would like to be able to write
Bar'Name.)

> For instance, in your current proposal, if I added a root exception to Claw,
> which then all of the other exceptions were derived from, that would make
> all of the Claw exceptions "exception types". If I then wrote the (common)
> handler:
>
>     when Info:Claw.Any_Claw_Error =>
>    Log_Item ("This routine - " &
> Ada.Exceptions.Exception_Information(Info));
>
> you're saying that this is illegal?? No way. I will vote against any
> proposal which restricts the use of Ada.Exceptions.Exception_Information or
> Ada.Exceptions.Exception_Name.

Keep cool, we are not at the voting stage yet :-)

Would Info'Exception_Information be more acceptable to you?  A critical
difference between the two is that by using an attribute we do not force the
implementation to use tagged types/class-wide types/dispatching, which might be
very destabilizing.  Of course internally it could work that way, but it
wouldn't have to.

> What I was proposing was that type Exception_Occurrence be tagged, so that
> Ada.Exceptions could be rewritten with appropriate classwide routines.
>
>     ...
>
> However, that has an inheritance problem. Probably we would need to
> introduce "Root_Exception_Occurrence", and make the global operations
> available on it.

It is full of compatibility problems: you cannot derive from
Exception_Occurrence in a nested scope, all derivations now need an extension,
etc.

> (I think it is a mistake to blur the distinction between an exception and an
> occurrence; they're clearly different things.)

In general I agree with this statement, and I was trying to make the difference
more apparent.  Obviously I failed...

> (And personally, I don't think this proposal is going any further than
> extensible enumeration types.)

Maybe, but it was on my action items list.  I would really like it if we could
improve the exceptions model of Ada at a reasonable implementation cost, but
this is a hard problem, especially with compatibility issues.

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

From: Randy Brukardt
Sent: Tuesday, May 15, 2001 12:21 AM

> Would Info'Exception_Information be more acceptable to you?  A critical
> difference between the two is that by using an attribute we do not force the
> implementation to use tagged types/class-wide types/dispatching, which might
> be very destabilizing.  Of course internally it could work that way, but it
> wouldn't have to.

That would be a solution, but of course it would require changing code (and
coding habits).

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

From: Robert Dewar
Sent: Saturday, October 26, 2002 10:25 AM

Here is a different subject entirely and I am really posting here to ask
whether anyone has registered this.

There is no restriction in Ada on raising exceptions in Pure units, i.e.
it is just fine for a pure subprogram to raise CE.

However, you can't raise an exception with a message because of the
arbitrary restriction imposed by the fact that Ada.Exceptions cannot be
Pure.

(once again, the mess with Pure/Preelaborate etc strikes and no, I am not
suggesting trying to fix this).

But not being able to raise exceptions with a message is really annoying.

I have appended at the bottom a horrible kludge package that GNAT provides
for achieving this in limited circumstances :-)

How about elevating messages to first class status (even when you can
WITH Ada.Exceptions it is kludgy to have to call Raise_Exception with
an identity attribute even the more so because due to bad design this
procedure can return :-(

Perhaps

   raise exception with message

e.g.

  raise Constraint_Error with "128-bit arithmetic overflow";

Here is the kludge package from GNAT:


------------------------------------------------------------------------------
--                                                                          --
--                         GNAT RUN-TIME COMPONENTS                         --
--                                                                          --
--                      G N A T . E X C E P T I O N S                       --
--                                                                          --
--                                 S p e c                                  --
--                                                                          --
--                            $Revision: 1.5 $
--                                                                          --
--           Copyright (C) 2000-2002 Ada Core Technologies, Inc.            --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
-- for  more details.  You should have  received  a copy of the GNU General --
-- Public License  distributed with GNAT;  see file COPYING.  If not, write --
-- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
-- MA 02111-1307, USA.                                                      --
--                                                                          --
-- As a special exception,  if other files  instantiate  generics from this --
-- unit, or you link  this unit with other files  to produce an executable, --
-- this  unit  does not  by itself cause  the resulting  executable  to  be --
-- covered  by the  GNU  General  Public  License.  This exception does not --
-- however invalidate  any other reasons why  the executable file  might be --
-- covered by the  GNU Public License.                                      --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
--                                                                          --
------------------------------------------------------------------------------

--  This package provides an interface for raising predefined exceptions
--  with an exception message. It can be used from Pure units.

--  There is no prohibition in Ada that prevents exceptions being raised
--  from within pure units. The raise statement is perfectly acceptable.

--  However, it is not normally possible to raise an exception with a
--  message because the routine Ada.Exceptions.Raise_Exception is not in
--  a Pure unit. This is an annoying and unnecessary restrictiona and this
--  package allows for raising the standard predefined exceptions at least.

package GNAT.Exceptions is
pragma Pure (Exceptions);

   type Exception_Type is limited null record;
   --  Type used to specify which exception to raise.

   --  Really Exception_Type is Exception_Id, but Exception_Id can't be
   --  used directly since it is declared in the non-pure unit Ada.Exceptions,

   --  Exception_Id is in fact simply a pointer to the type Exception_Data
   --  declared in System.Standard_Library (which is also non-pure). So what
   --  we do is to define it here as a by reference type (any by reference
   --  type would do), and then Import the definitions from Standard_Library.
   --  Since this is a by reference type, these will be passed by reference,
   --  which has the same effect as passing a pointer.

   --  This type is not private because keeping it by reference would require
   --  defining it in a way (e.g a tagged type) that would drag other run time
   --  files, which is unwanted in the case of e.g ravenscar where we want to
   --  minimize the number of run time files needed by default.

   CE : constant Exception_Type;  -- Constraint_Error
   PE : constant Exception_Type;  -- Program_Error
   SE : constant Exception_Type;  -- Storage_Error
   TE : constant Exception_Type;  -- Tasking_Error
   --  One of these constants is used in the call to specify the exception

   procedure Raise_Exception (E : Exception_Type; Message : String);
   pragma Import (Ada, Raise_Exception, "__gnat_raise_exception");
   pragma No_Return (Raise_Exception);
   --  Raise specified exception with specified message

private
   pragma Import (C, CE, "constraint_error");
   pragma Import (C, PE, "program_error");
   pragma Import (C, SE, "storage_error");
   pragma Import (C, TE, "tasking_error");
   --  References to the exception structures in the standard library

end GNAT.Exceptions;

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

From: Robert I. Eachus
Sent: Saturday, October 26, 2002  4:14 PM

>However, you can't raise an exception with a message because of the
>arbitrary restriction imposed by the fact that Ada.Exceptions cannot be
>Pure.
>
How difficult would it be to fix this by making Ada.Exceptions pure?  As
I see it, this is a question for implementors.  As far as I am
concerned, this is one of those packages that should have been pure to
begin with.

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

From: Robert Dewar
Sent: Saturday, October 26, 2002  4:33 PM

It is nowhere near a pure package, either conceptually or actually.
It has access types in it so that's decisive in any case.

For fun I tried compiling Ada.Exceptions with pragma Pure. Here is what I
get:


    68.    type Subprogram_Descriptor_List_Ptr is
                |
        >>> named access type not allowed in pure unit

    71.    Subprogram_Descriptors : Subprogram_Descriptor_List_Ptr;
           |
        >>> declaration of variable not allowed in pure unit

    83.    Num_Subprogram_Descriptors : Natural;
           |
        >>> declaration of variable not allowed in pure unit

    89.    Exception_Tracebacks : Integer;
           |
        >>> declaration of variable not allowed in pure unit

    94.    Zero_Cost_Exceptions : Integer;
           |
        >>> declaration of variable not allowed in pure unit

   227.        (Subprogram_Descriptor_List, Subprogram_Descriptor_List_Ptr);
                                            |
        >>> named access types not allowed in pure unit

   864.    Counter : Unsigned := 0;
           |
        >>> declaration of variable not allowed in pure unit

==============Error messages for source file: a-except.ads
    54.    type Exception_Occurrence_Access is access all Exception_Occurrence;
                |
        >>> named access type not allowed in pure unit

   118.    subtype EOA is Exception_Occurrence_Access;
           |
        >>> named access types not allowed in pure unit

   347.      Id               => Null_Id,
                                 |
        >>> non-static constant in preelaborated unit

   354.      Tracebacks       => (others => Null_Loc));
                                            |
        >>> non-static constant in preelaborated unit


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

From: Robert Dewar
Sent: Saturday, October 26, 2002  4:36 PM

Robert (Eachus), remember that even System itself is not guaranteed is
required to be Pure, there is implementation permission to allow it to
be Pure but this is not required.

(there's a little cleanup that should be considered, require System to
be Pure, I wouldn't be surprised if everyone does that already).

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

From: Robert I. Eachus
Sent: Sunday, October 27, 2002  7:17 PM

>It is nowhere near a pure package, either conceptually or actually.
>It has access types in it so that's decisive in any case.

Access type actually, I overlooked Exception_Occurance_Access.
(Parenthetically, I don't know why "access all" has to be verboten in
Pure units, because there is no requirement for an associated collection.)

But that is all detail.  The important answer is the one Robert gave as
an implementor, which indicates a lot of buried impurity.

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

From: Robert Dewar
Sent: Sunday, October 27, 2002  7:38 PM

And I think in practice this kind of buried impurity will be typical, since
there is a lot of conceptually not very pure stuff here. I would hardly
regard Ada.Eceptions as a collection of mathematically well behaved
functions :-)

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

From: Tucker Taft
Sent: Saturday, October 26, 2002  5:33 PM

This seems like a nice, simple addition to the language.
Basing it on "purity" requirements is clever also ;-).
(Robert the Pure...)

Robert Dewar wrote:

> ...
>
> Perhaps
>
>    raise exception with message
>
> e.g.
>
>   raise Constraint_Error with "128-bit arithmetic overflow";

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

From: Robert Dewar
Sent: Saturday, October 26, 2002  6:00 PM

Well it was not some kind of clever idea, but rather a reaction to a real
need :-)

We like to give messages with all exceptions, and for example our pure
64-bit arithmetic-with-overflow package could not do this without a
junk package.

The trouble is that the kludge approach is not easy to extend, because
there is no simple way of passing an exception. You can't use
Exception_Name'Identity because that type is defined in the same
annoying impure package :-)

I suppose we could introduce a new attribute that gave an address value
that could be passed to a pure procedure, or even an instance of some
bogus type in the kludge package (UGH!)

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

From: Nick Roberts
Sent: Friday, March 25, 2005  6:27 AM

AI 264 proposed a significant extension to the Ada exception mechanism,
permitting a new exception type to be declared that allowed arbitrary data
to accompany an exception occurrence.

This proposal seems to have been unanimously, presumably because the ARG
felt it was overkill (and I agree).

However, this proposal was made to solve a real problem (the passing of data
with an exception, beyond what is practicable using strings), and the
problem remains. In particular, the OMG IDL (of CORBA) mapping into Ada will
need to be updated for Ada 2005, and this presents an opportunity to improve
the currently awful mapping of exception handling.

The proposal of AI 264 would have solved this problem, but something less
heavy would also help. I propose an extra package:

   with Ada.Streams;
   package Ada.Exceptions.Parametized is

      type Params_Stream_Type(
         Initial_Size : Ada.Streams.Stream_Element_Count) is
         new Ada.Streams.Root_Stream_Type with private;

      procedure Read(
         Stream : in out Params_Stream_Type;
         Item : out Ada.Streams.Stream_Element_Array;
         Last : out Ada.Streams.Stream_Element_Offset);

      procedure Write(
         Stream : in out Params_Stream_Type;
         Item : in Ada.Streams.Stream_Element_Array);

      procedure Raise_Exception(
         E : in Exception_ID;
         Params  : not null access Params_Stream_Type;
         Message : in String := "");

      function Has_Parameters(
         X : Exception_Occurrence) return Boolean;

      function Exception_Parameters(
         X : Exception_Occurrence) return access Params_Stream_Type;

   private
      ...
   end Ada.Exceptions.Parametized;

The idea is that this package allows a stream to be associated with an
exception (when it is raised), into which the associated data can be
written, so that when the exception is caught and handled, the data can be
read from the stream. Because the raiser must allocate (space in memory for)
the stream, the implementation does not have to deal with this problem.

Could this be made an optional part of the standard, or a secondary
standard?

Also, at the end of the discussion, RBKD suggested that the 'raise'
statement's syntax be extended to allow the exception message to be
provided. This would be especially useful in pure packages, which cannot
'with' Ada.Exceptions (because it is not pure). Has this idea also been
conscientiously dropped?

I'm really just wanting to spark a discussion on this subject.

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

From: Pascal Leroy
Sent: Friday, March 25, 2005  7:50 AM

I have remained frustrated by this.  AI 264 led us down a path of
inextricable complexity, and killing it was the right thing to do.  On the
other hand the problem it was trying to address is real, and it is
unfortunate that Ada 2005 is not going to bring much progress in this
area.  This is one of these very difficult problems (like limited with or
interfaces) that would have required a great deal of time and energy and
many iterations to properly address.  Obviously we didn't have the stamina
(or we thought that other topics were more important).

> The proposal of AI 264 would have solved this problem, but
> something less heavy would also help. I propose an extra package:
>
> ...
>
> The idea is that this package allows a stream to be
> associated with an exception (when it is raised), into which
> the associated data can be written, so that when the
> exception is caught and handled, the data can be read from
> the stream. Because the raiser must allocate (space in memory
> for) the stream, the implementation does not have to deal
> with this problem.

I have seen users who were doing basically that, except that they were
encoding the stream as part of the exception message.  If the compiler is
friendly enough to support biggish exception messages this works fine, and
it can be written without any support from the language or the compiler.

> Could this be made an optional part of the standard, or a
> secondary standard?

The standard is in the process of being finalized, so certainly not in
2005.  Maybe next time.  As for a secondary standard, sure, if someone
feels like starting an international workshop agreement to get things
going.

> Also, at the end of the discussion, RBKD suggested that the
> 'raise' statement's syntax be extended to allow the exception
> message to be provided. This would be especially useful in
> pure packages, which cannot 'with' Ada.Exceptions (because it
> is not pure). Has this idea also been conscientiously dropped?

Look at AI 361 (or better, look at section 11.3 in the draft RM at
http://www.adaic.com/standards/ada06.html).  I think it does just what you
want.

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

From: Marius Amado Alves
Sent: Friday, March 25, 2005  7:02 AM

Why streams? Because of middlewares? Why not a generic parameter?
Because of language profiles that forbid generics?

    generic

       type Parameter is private (<>);

    package Ada.Exceptions.Parametrized is

       procedure Raise_Exception(
          E : in Exception_ID;
          Param : Parameter;
          Message : in String := "");

       function Exception_Parameter(
          X : Exception_Occurrence) return Parameter;
    ...

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

From: Pascal Leroy
Sent: Saturday, March 26, 2005  3:53 AM

At a high-level you can view it that way, I suppose, but this is fraught
with difficulties, because of issues with controlled types, masters, etc.
Say that Parameter is a controlled type.  During Raise_Exception, when
does it get finalized?  In a handler, do you get a finalized object?  If
not, what is its master?  These are the questions (among others) that
killed AI 264.

The advantage of a stream is that it's essentially an array of bytes, and
most of those issues disappear.

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


Questions? Ask the ACAA Technical Agent