CVS difference for ais/ai-00279.txt

Differences between 1.3 and version 1.4
Log of other versions for file ais/ai-00279.txt

--- ais/ai-00279.txt	2002/02/05 02:06:24	1.3
+++ ais/ai-00279.txt	2002/04/26 20:15:17	1.4
@@ -335,3 +335,170 @@
 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