Version 1.11 of ais/ai-00279.txt

Unformatted version of ais/ai-00279.txt version 1.11
Other versions for file ais/ai-00279.txt

!standard 13.13.02 (34)          04-11-08 AI95-00279/07
!standard 3.09 (12)
!standard 3.09 (26)
!standard 13.13.02 (36/1)
!standard 13.13.02 (38)
!standard 13.14 (19/1)
!class binding interpretation 01-12-21
!status Amendment 200Y 04-09-24
!status WG9 approved 04-11-18
!status ARG Approved 7-0-2 04-09-18
!status work item 01-12-21
!status received 01-11-08
!qualifier Omission
!priority Medium
!difficulty Medium
!subject Tag read by T'Class'Input
!summary
If Ada.Tags.Internal_Tag or Ada.Tags.Descendant_Tag is given a string identifying a library-level type which has not yet been frozen, Tag_Error is raised.
If the tag identified by T'Class'Input identifies a type:
-- that is the tag of some type not covered by T'Class, Constraint_Error
is raised;
-- that is the tag of an abstract type, Constraint_Error is raised; -- that is the tag of a type which currently does not exist in the
partition, the program is erroneous. (This can only happen for local tagged types; library level types either have been frozen or Tag_Error is raised when reading the tag.)
!question
T'Class'Input reads in a string, maps it to a tag by calling Tags.Descendant_Tag, and dispatches accordingly.
What happens if the call to Tags.Descendant_Tag (from AI-344, or Tags.Internal_Tag without it) yields either
a) the tag of some type not covered by T'Class
or
b) the tag of an abstract type
or
c) the tag of a type whose freezing point has not
yet been elaborated (e.g. a type with a not-yet-elaborated component subtype)?
!recommendation
(See summary.)
!wording
Add to 3.9(12) and 3.9(12.1) (the first paragraph of the insertion from AI-344):
Tag_Error is also raised if the specific type identified is a library-level type whose tag has not yet been created.
Add "or external tag" to 3.9(26), so that the string parameters of Internal_Tag and Descendant_Tag are clearly covered.
Add to 13.13.2(34):
If the specific type identified by the internal tag is not covered by T'Class or is abstract, Constraint_Error is raised.
Add after 13.13.2(36/1) (If AI-344 is approved, replace Internal_Tag by Descendant_Tag in the wording below):
Erroneous Execution
If the internal tag returned by Internal_Tag to T'Class'Input identifies a specific type whose tag has not been created, or does not exist in the partition at the time of the call, execution is erroneous.
AARM Note
The definition of Internal_Tag prevents such a tag from being provided to T'Class'Input if T is a library-level type. However, this is needed for nested tagged types.
Add after 13.13.2(38):
33 If the prefix subtype S of function S'Class'Input is a library-level subtype, then reading a value of a type which has not yet been frozen with the S'Class'Input function will always raise Tag_Error; execution cannot be erroneous.
Add after 13.14(19/1):
Dynamic Semantics
The tag (see 3.9) of a tagged type T is created at the point where T is frozen.
!discussion
The exact wording of 13.13.2(34/1) is "...dispatches to the subprogram denoted by the Input attribute of the specific type identified by the internal tag; returns that result.". Reading this strictly, we would expect the appropriate 'Input to be called (by dispatching), followed by a conversion to the return type. That is, somewhere there is code like:
return T'Class(Dispatching_Constructor (Tags.Descendant_Tag(...),
'Input(Stream));
(where Dispatching_Constructor is a magic routine that dispatches to the appropriate routine identified by the tag in its first parameter; AI-260-2 proposes to standardize such a routine).
If the tag dispatched on is not covered by T, then this final conversion would raise Constraint_Error. Therefore, we simply confirm the existing language by saying that Constraint_Error is raised in this case. However, there is no benefit in insisting that the call to the 'Input routine is actually made, so we just clarify this to say that Constraint_Error is raised.
If the tag dispatched on is an abstract type, then the appropriate 'Input is called. The language says that this 'Input operation exists, but that it cannot be called because it is not available. (See AI-195 for the definition of "available".)
We certainly cannot allow a non-available routine to be dispatched to. Thus, there must be a runtime check for this case, and an exception raised. We choose to raise Constraint_Error to be similar to the previous case.
If the tag dispatched on is for a type that has not yet been frozen, we could have an access-before-elaboration problem. Making a check in this case could be difficult. The type may be nested, or not even declared yet, so the normal implementation solution of associating a Boolean value with the type does not work.
Thus, the only practical solution is for the use of such a tag to be erroneous. This is a rather unfortunate case of erroneousness, however, since it requires a particular flow of control to occur. A perfectly safe subprogram can be called from elaboration code, and suddenly cause this erroneousness to occur. Thus, to insure that this does not occur would require either full program flow analysis, or completely avoiding T'Class'Input. Luckily, we can mitigate this problem.
The problem is really that Descendant_Tag is returning the tag for a type that is not frozen or does not currently exist. If Descendant_Tag is implemented dynamically, this cannot happen (such a type must necessarily raise Tag_Error). However, if Descendant_Tag is implemented partially statically, this can happen.
We can mandate that Descendant_Tag behave as if tags are allocated dynamically. For a static implementation, this only requires a Boolean value associated with each tag; For Descendant_Tag, if the value is False for the appropriate tag, Tag_Error is raised, otherwise the tag is returned. However, this approach runs into problem with nested tagged types, which may be elaborated by multiple tasks at the same time. In this case, a single Boolean does not work.
AI-344 allows type extensions that are more nested than the original type. This AI also defines that the tag is sufficient to distinguish the descendant from all other descendants of the same type (even if they are from different elaborations of the same type declaration). This implies that Descendant_Tag will have at least some dynamic characteristics; a fully static implementation would be incorrect. This means that we could insist on checks for any types that have a library-level ancestor.
However, nested tagged types of all kinds can be "de-elaborated" as well as elaborated. That would complicate the rule. Moreover, AI-344 is careful not to require implementation changes for nested tagged types not extended from some less-nested ancestor. Therefore, they could still be implemented with a static scheme, where making the check is impossible due to race conditions and multiple elaborations. Finally, AI-344 does not require Descendant_Tag to be able to tell between instances of the same type. Thus, it still would be possible to read the tag and confuse it with some other instance of the type (if the prefix type to T'Class'Input is more nested than some ancestor).
The primary purpose of the Tag_Error rule for Ada.Tags is to insure that a static analysis of a program (without control flow information) can prove that there are no erroneous uses of T'Class'Input. For this purpose, requiring checks for library-level types is sufficient. If the user insures that all calls to T'Class'Input have a library-level type as the prefix, then no erroneousness can occur: either the tag has been created, or Tag_Error is raised if the tag is prematurely read. If the tag of a more nested descendant type is read, Descendant_Tag is defined to raise Tag_Error (whether or not the type exists currently). This can be checked by a static analysis tool without the need to simulate control flows.
Therefore, we define that a tag is created at the freezing point of the tagged type, and then adopt the rule that Descendant_Tag must raise Tag_Error if the string identifies a library-level type whose tag has not been created. This rule allows a single Boolean value to be used in a static implementation, and does not have any race conditions.
The Internal_Tag function also has this rule, since it eliminates erroneousness from user-written routines (including instances of Generic_Dispatching_Constructor, see AI-260-2) and there is no additional implementation burden.
The wording for erroneous execution includes "does not exist" so that a type which existed earlier but now does not exist is covered. Such a type can only be a problem if it is derived from a type which now exists and is at the same nesting level (it presumably is declared just below the call to T'Class'Input).
We considered simplifying the check to make it easier to implement. The initial suggestion was to have an implementation permission for Internal_Tag to raise Tag_Error if the main program has not yet started execution. This is easy to implement, and has the added benefit (when used) of insuring that the problem is caught early. However, used alone, it reintroduces the hole, because an implementation not taking advantage of the permission could still return the tag of an unfrozen type.
Alternatively, we could simply always require this behavior. That would simplify the wording. But it also would mean that any use of T'Class'Input during elaboration time would raise Tag_Error (whether or not a problem is possible). That would mean that T'Class'Input could not be used to read configuration files and the like. That seems like too strong a restriction.
!corrigendum 3.09(12)
Replace the paragraph:
The function Internal_Tag returns the tag that corresponds to the given external tag, or raises Tag_Error if the given string is not the external tag for any specific type of the partition.
by:
The function Internal_Tag returns a tag that corresponds to the given external tag, or raises Tag_Error if the given string is not the external tag for any specific type of the partition. Tag_Error is also raised if the specific type identified is a library-level type whose tag has not yet been created.
!corrigendum 3.09(26)
Replace the paragraph:
The implementation of the functions in Ada.Tags may raise Tag_Error if no specific type corresponding to the tag passed as a parameter exists in the partition at the time the function is called.
by:
The implementation of the functions in Ada.Tags may raise Tag_Error if no specific type corresponding to the tag or external tag passed as a parameter exists in the partition at the time the function is called.
!corrigendum 13.13.02(34)
Replace the paragraph:
First reads the external tag from Stream and determines the corresponding internal tag (by calling Tags.Internal_Tag(String'Input(Stream)) -- see 3.9) and then dispatches to the subprogram denoted by the Input attribute of the specific type identified by the internal tag; returns that result.
by:
First reads the external tag from Stream and determines the corresponding internal tag (by calling Tags.Internal_Tag(String'Input(Stream)) -- see 3.9) and then dispatches to the subprogram denoted by the Input attribute of the specific type identified by the internal tag; returns that result. If the specific type identified by the internal tag is not covered by T'Class or is abstract, Constraint_Error is raised.
!corrigendum 13.13.02(36/1)
Insert after the paragraph:
The stream-oriented attributes may be specified for any type via an attribute_definition_clause. All nonlimited types have default implementations for these operations. An attribute_reference for one of these attributes is illegal if the type is limited, unless the attribute has been specified by an attribute_definition_clause or (for a type extension) the attribute has been specified for an ancestor type. For an attribute_definition_clause specifying one of these attributes, the subtype of the Item parameter shall be the base subtype if scalar, and the first subtype otherwise. The same rule applies to the result of the Input function.
the new paragraph:
Erroneous Execution
If the internal tag returned by Internal_Tag to T'Class'Input identifies a specific type whose tag has not been created, or does not exist in the partition at the time of the call, execution is erroneous.
!corrigendum 13.13.2(38)
Insert after the paragraph:
32 User-specified attributes of S'Class are not inherited by other class-wide types descended from S.
the new paragraph:
33 If the prefix subtype S of function S'Class'Input is a library-level subtype, then reading a value of a type which has not yet been frozen with the S'Class'Input function will always raise Tag_Error; execution cannot be erroneous.
!corrigendum 13.14(19/1)
Insert after the paragraph:
An operational or representation item that directly specifies an aspect of an entity shall appear before the entity is frozen (see 13.1).
the new paragraph:
Dynamic Semantics
The tag (see 3.9) of a tagged type T is created at the point where T is frozen.
!ACATS test
A C-Test should be written to test these cases (other than the erroneous case).
!appendix

From: Steve Baird
Date: Thursday, November 08, 2001   8:54 PM

T'Class'Input reads in a string, maps it to a tag,
and dispatches accordingly.

What happens if the the call to Tags.Internal_Tag yields either
   a) the tag of some type not covered by T'Class
or
   b) the tag of an abstract type
or
   c) the tag of a type whose freezing point has not
      yet been elaborated (e.g. a type with a
      not-yet-elaborated component subtype).
?

Is this erroneous? Must Program_Error be raised?

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

From: Randy Brukardt
Date: Wednesday, November 14, 2001   7:26 PM

> T'Class'Input reads in a string, maps it to a tag,
> and dispatches accordingly.
>
> What happens if the the call to Tags.Internal_Tag
> yields either
>    a) the tag of some type not covered by T'Class

The exact wording of 13.13.2(34) is "...dispatches to the subprogram denoted
by the Input attribute of the specific type identified by the internal tag;
returns that result.". Reading this strictly, I would expect the appropriate
'Input to be called, then Constraint_Error to be raised because it isn't
possible to convert the result to the return type. That is, somewhere this
is code like:

    return T'Class(Call_by_Tag (Tags.Internal_Tag(...), 'Input(....));

But we may want to change that. Note that I already pointed out that this
isn't clearly defined in my (old) write-up of AI-260 ('Tag_Read).

> or
>    b) the tag of an abstract type

Here again, it is clear what happens: if the abstract type is Abstr,
Abstr'Input is called. Note that Abstr'Input is defined. AI-195 says that it
exists, but cannot be called, because it is not "available".

So we seem to have a case where a routine that is not "available" can be
dispatched to.

> or
>    c) the tag of a type whose freezing point has not
>       yet been elaborated (e.g. a type with a
>       not-yet-elaborated component subtype).
> ?

Sigh. If the 'Input is user-defined, it couldn't have been elaborated yet,
so Program_Error must be raised. But are predefined 'Input elaborated? (I
hope not - we don't need the overhead). If not, then we have an ugly and
highly unlikely case. Probably it is best to just declare it erroneous, as
any check is going to be expensive and distributed.

> Is this erroneous? Must Program_Error be raised?

The answers to these questions probably are all different. I think (a)
should be Constraint_Error, and in any case, should be the same as whatever
AI-260 says happens for 'Tag_Read.

(b) is a special case which will have to raise some exception; probably
Constraint_Error or Program_Error.

(c) seems best to be erroneous, because the check is going to occur on every
use of a stream attribute, and this is very rare. But when it can be
checked, it should raise Program_Error (as it is an
access-before-elaboration error).

In any case, I don't think this new issue should be used to prevent AI-195
from being completed. That AI already has too many issues in it, and if we
stick every stream issue that we discover (and I expect our grandchildren
will still be discovering problems with streams) into it, we'll never finish
it and fix many important stream problems.

Besides, issue (a) was already discussed in the context of AI-260, and it
would make just as much sense to put this issue there. Or open a new AI.

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

From: Steve Baird
Date: Monday, January 28, 2002   2:27 PM

To recap, AI-279 is a response to the following question:

    T'Class'Input reads in a string, maps it to a tag by calling
    Tags.Internal_Tag, and dispatches accordingly.

    What happens if the the call to Tags.Internal_Tag yields either
      a) the tag of some type not covered by T'Class
    or
      b) the tag of an abstract type
    or
      c) the tag of a type whose freezing point has not
         yet been elaborated (e.g. a type with a
         not-yet-elaborated component subtype)?

The answers given in the AI summary are
    a) raise Constraint_Error
    b) raise Constraint_Error
    c) bounded error: raise Program_Error or "the call works normally".

For a & b, this is fine. One might argue for raising Program_Error instead
of Constraint_Error, but that's a nit. It is important to be clear that
no dispatching call occurs if the exception is raised; the check precedes
the call.

I believe the third case should result in erroneous execution.

The suggestion that "the call works normally" or works "as defined by the
RM"
is not well defined. The language (specifically, the freezing rules)
statically prohibits the creation of an object of an unfrozen type. The
dynamic
semantics of a statically illegal construct are undefined. With the possible
exception of the rules pertaining to erroneous execution, the RM does not
have rules of the form "<blap> is prohibited, but this is what it does at
runtime if you do somehow manage to get it through the compiler".

Allowing non-erroneous execution in this case might allow a subprogram to
be called before it can be named (i.e. before the elaboration of its
initial declaration). This would be new to Ada. It could mean,
for example, that a one-part subprogram might require an elaboration
check, or that an elaboration checking implementation strategy based on
initializing some piece of state at the point of the initial declaration
of a subprogram and then updating it when the body is elaborated would
no longer work.

Requiring implementations to detect the error and raise some exception
would also be a well-defined solution, but would require a fair
amount of overhead (in execution time, code space, and implementation
complexity) to cope with a case which, as far as I know, noone
has ever run into in practice.

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

From: Robert Dewar
Date: Monday, January 28, 2002   3:45 PM

I agree with Steve's position on this point, case c) should be erroneous.

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

From: Randy Brukardt
Date: Monday, January 28, 2002   5:07 PM

> I agree with Steve's position on this point, case c) should
> be erroneous

I made this a bounded error when I wrote up the AI, because I didn't like
the extent of erroneousness that is caused.

If this is erroneous, then *any* call to 'Input before all tagged types are
elaborated is potentially erroneous. Moreover, there is no way to prevent
erroneous execution in that case, because the programmer has no control over
the data actually read. And, there is no way to detect that there was/might
be a problem, until you've run off the tracks.

To prevent the erroneousness, you would have to have elaborate pragmas for
every tagged type that the 'Input might read, which completely defeats the
purpose of classwide 'Input (and would require adding such pragmas during
maintenance as tagged types are added).

The only safe thing to do would be to avoid using classwide 'Input until the
main subprogram starts executing (assuming that all tagged types are library
level, which is almost always the case). But in reusable code, there would
be no way to enforce this prohibition, and no way to prevent catastrophe if
it happened.

So while I'm uncomfortable spending much effort on a rare case, making this
erroneous would make using 'Input unsafe in any high-reliability system.

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

From: Robert Dewar
Date: Monday, January 28, 2002   9:19 PM

<<The only safe thing to do would be to avoid using classwide 'Input until the
main subprogram starts executing (assuming that all tagged types are library
level, which is almost always the case). But in reusable code, there would
be no way to enforce this prohibition, and no way to prevent catastrophe if
it happened.
>>

Indeed that's the only safe thing to do.

<<So while I'm uncomfortable spending much effort on a rare case, making this
erroneous would make using 'Input unsafe in any high-reliability system.
>>

If you don't want this to be erroneous, you have a lot more work to do in
describing what the possible behaviors are. You can't appeal to the "proper
RM behavior" here, as was pointed out earlier in the thread.

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

From: Randy Brukardt
Date: Monday, January 28, 2002   9:47 PM

> <<So while I'm uncomfortable spending much effort on a rare case, making this
> erroneous would make using 'Input unsafe in any high-reliability system.
> >>
>
> If you don't want this to be erroneous, you have a lot more work to do in
> describing what the possible behaviors are. You can't appeal to the "proper
> RM behavior" here, as was pointed out earlier in the thread.

Steve was reacting to my informal description of the semantics. I made no
attempt to determine the RM wording, since I felt that we needed to discuss
this at an ARG meeting before putting in that effort.

If it turns out to be impossible to word, then I probably would lean toward
requiring an access before elaboration check. My original reaction to that was
(as Steve put it) "would require a fair amount of overhead (in execution time,
code space, and implementation complexity)", but I've since concluded that
doing so is no worse than the elaboration check that is needed on every
user-defined 'Input routine anyway. The only requirement would be that default
'Input routines actually exist somewhere, which I believe is necessary anyway
for dispatching. The question is whether the (small) overhead is worth it; Ada
concludes that is the case for regular subprograms, so it seems hard to argue
that it shouldn't be the case for the default implementation of 'Input.

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

From: Randy Brukardt
Date: Monday, April 22, 2002  10:36 PM

At the Cupertino meeting, we discussed this issue. In particular, we discussed
case (c) (If Internal_Tag returns the tag of a type whose freezing point has
not yet been elaborated?)

The discussion at the meeting purported to demonstrate that doing an
elaboration check to detect this case is difficult. Given that, I
reluctantly agreed to let this case be erroneous. I also got assigned to
revise the AI.

In writing the minutes and in preparing to revise the AI, I have realized
that there is in fact a reasonably inexpensive way to make this check.
Moreover, I also realized that there is another way to prevent this problem
altogether. And this case seems to show a case where Ada's default behavior
is unsafe.

There also is an additional, similar problem not covered by the AI as it
stands. Case (c2):
(If Internal_Tag returns the tag of a type which was elaborated but no
longer exists (because the master it was defined in has been left)?)

My objection to making this erroneous is simply that this is an unusual case
of erroneousness, where neither the use of 'Input nor anything that the
caller of it does is wrong. The problem only occurs when a conjunction of
unfortunate occurrences happens. This is a problem (especially in
pre-packaged libraries), as the only way to verify the absence of this
problem would be full program call graph analysis. In addition, this case is
by far the most likely of any of the cases considered by this AI. (Consider
reading a configuration file.) Abort can cause erroneousness like this, but
everyone knows abort is unsafe (and it is easy to see if it is used, just
search for "abort" in your program) -- this is not true for 'Class'Input.

3.9 is amazingly vague on the description of how Internal_Tag works. That
probably is a consequence of not wanting to constrain the implementations
much.

When thinking about how this problem would occur in an implementation using
dynamically constructed tags, I realized that such an implementation could
not have this problem. Such an implementation would have to register the
tags with some sort of tag manager when they are created (presumably at the
freezing point for the type). That means that any call to Internal_Tag
before the type is elaborated would raise Tag_Error -- and no problem could
occur.

A similar (but simpler) implementation would work for the 'Input check. An
array of booleans, one for each tag, would be stored with (logically) the
tag data structure. When the type is frozen, the associated Boolean is set
to true (these addresses can be set at compile time). The Boolean would be
set to False if the type went out of scope. If the Boolean is False, the
type is non-existent, and Program_Error should be raised.

But this brings up the question: is it really a good idea for Internal_Tag
to be returning a tag for a non-existent type? The AI intends to fix the
problem only for 'Class'Input. That is somewhat OK, as 'Class'Input is the
only Ada 95 place where the result of Internal_Tag can be usefully used (for
instance, to dispatch on). One could hope that Ada 0y would provide a real
solution to the problem is dispatching on externally derived tags, so that
functions like T'Class'Input could be written by users when needed. If such
a facility is added to Ada, it would have the same problem.

3.9 is quite silent on the lifetime of tags. This appears to be a
consequence of preferring a static model for tags. This model, while it must
be allowed in some way, is rather at odds with Ada's primarily dynamic model
of lifetimes. Thus, we get problems like the one covered in this AI, where
we can get a reference to something that doesn't yet (or still) exist.

About the only place where the RM ever talks about the lifetime of tags is
the Implementation Permission in 3.9(26):

The implementation of the functions in Ada.Tags may raise Tag_Error if no
specific type corresponding to the tag passed as a parameter exists in the
partition at the time the function is called.

This is a rather curious paragraph. It says that it would be OK for an
implementation to raise an exception if the type corresponding to a tag does
not exist, implying that it is OK to NOT raise an exception for a
non-existent type. Ada generally takes a safety-first approach, but here we
allowing (in fact encouraging) unsafe behavior.

This paragraph would have been better written as a bounded error, allowing
an implementation to return the correct answer:

It is a bounded error if one of the functions in Ada.Tags is called when no
specific type corresponding to the tag passed as a parameter exists in the
partition. If the error is detected, Tag_Error is raised; otherwise, the
function returns the same result that it would have had the type existed.

This would have been a better flag that there was an unsafe permission.

In any case, the overhead of making this check is rather small (one bit set
at the freezing of the type, one bit checked after a successful lookup in
Internal_Tag). A correct Ada application cannot depend on this working, as
3.9(26) already allows the check. Thus, I believe this check should be
mandated; doing do would eliminate case (c) [and (c2)] without
erroneousness. This is easy to accomplish: change "may" to "shall" in
3.9(26).

I could imagine a partial version of this check, where we don't mandate the
check for types whose masters are left. Such types are exceedingly rare, and
almost nothing useful can be done with them (at least in terms of
dispatching), since the entire hierarchy needs to be inside of the scope. In
such a case, we'd leave erroneousness for case (c2), but case (c) [the more
likely case] could not happen. That would eliminate the overhead of 'turning
off' existence bits when a tagged type goes out of scope. The only downside
of this is more RM wording.

In practice, these are probably the same, as an ACATS test checking that
nested tagged types 'disappear' from Ada.Tags.Internal_Tag couldn't be
justified from a usage-based standpoint (such a program is very unlikely to
occur in practice).

I prefer any of these solutions over making this case unconditionally
erroneous. Requiring this check also would help if any additional uses for
tags are added to Ada.

Comments welcome (now donning flame-proof suit...)

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

From: Tucker Taft
Date: Tuesday, April 23, 2002  9:04 AM

What you propse seems reasonable.  A more detailed writeup
would help.  We implement Internal_Tag by statically allocating
a "link" which we insert into the hash table the first time
the type is elaborated.  It remains there indefinitely.
So we would have no trouble dealing with references
that occur before the type was ever elaborated, but
we do not currently ever remove the link from the table.
Doing so would be extra work, though not a huge amount.

I would imagine that some compilers build the Internal_Tag
hash table at link time.  If they all build them dynamically
the way that we do, then certainly providing some protection
against premature access to library-level tags would be easy
to provide.  Doing anything special for local tags seems
more trouble than it is worth.

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

From: Steve Baird
Date: Tuesday, April 23, 2002  10:03 PM

> There also is an additional, similar problem not covered
> by the AI as it stands. Case (c2):
> (If Internal_Tag returns the tag of a type which was
> elaborated but no longer exists (because the master it was
> defined in has been left)?)

T'Class'Input reads in a string, maps it to a tag by calling
Tags.Internal_Tag, and dispatches accordingly.

3.9.1(3,4) implies that if T'Class covers some type T2, then
T2 will not cease to exist before T. While calling
T'Class'Input, clearly T still exists.

Thus, if Tags.Internal_Tag yields the tag of a type which no
longer exists, then it must be the tag of some type not
covered by T'Class, so this is already covered by case a).

I agree that this case may pose special challenges for
implementations which allocate tags dynamically.

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

Questions? Ask the ACAA Technical Agent