!standard 3.9(12.1/2) 09-12-08 AI05-0113-1/03 !standard 3.9(25.3/2) !standard 3.9(26.1/2) !standard 13.3(76) !class binding interpretation 08-10-06 !status work item 08-10-06 !status received 08-06-13 !priority Low !difficulty Easy !qualifier Omission !subject Conflicting external tags and other tag issues !summary If a user-specified external tag S'External_Tag is the same as T'External_Tag for some other type, execution of the partition is erroneous. The Implementation Advice for Internal_Tag suggests returning a tag for a type that belongs to some master of the call to Internal_Tag. !question (1) If users specify values for 'External_Tags, it's possible that the external tags for two different type declarations may be the same, probably by accident. What is returned from Tags.Internal_Tag and Tags.Descendant_Tag in that case? (2) 3.9(26/2) says that Tag_Error can be raised if there are no types whose innermost master is a master of the point of the function call, but 3.9.(26.1/2) suggests that Internal_Tag return a tag of a type whose tag is *the* innermost master of the point of the call. But the innermost master is usually the function call itself, which surely does not have an innermost master. What is the intent? !recommendation (See Summary.) !wording Add before 3.9(25.3/2): Bounded (run-time) Errors It is a bounded error if the value of External passed to Tags.Internal_Tag or Tags.Descendant_Tag identifies more than one tagged type with the appropriate ancestor and accessibility level. Either Program_Error is raised, or one of the identified tagged types is returned. AARM Note: This can happen if a generic body contains a derivation of a tagged type declared outside of the generic, and there are multiple instances at the same accessibility level. (There is an exception to the general requirement that default external tags be unique for this case.) If Program_Error is not raised, and the returned tag is used by 'Input to read a stream, execution can become erroneous (the value may be read with the wrong type). [Editor's note: I don't exactly know what we should say about this case, but it seems wrong to remain silent. This is a real problem as a container instance instantiated in a generic body would not be required to have a unique external tag -- see 13.3(76). The other option is to delete the "except" wording from 13.3(76), perhaps that is a better idea (although it could be an implementation hardship for certain Ada compilers starting with 'J'. Be we could use the same scheme we use for generic body exceptions (we hash the name with the location of the instance parameter block, which makes it unique, but not readable).] Modify 3.9(26.1/2) as follows: Internal_Tag should return the tag of a type{, if one exists,} whose innermost master is {a}[the] master of the point of the function call. AARM Note: There is no Advice for the result of Internal_Tag if no such type exists. In most cases, the Implementation Permission can be used to raise Tag_Error, but some other tag can be returned as well. Add before 13.3(76): Dynamic Semantics If a user-specified external tag S'External_Tag is the same as T'External_Tag for some other tagged type declared in the partition, Program_Error is raised by the elaboration of the attribute_definition_clause. AARM Ramification This rule does not depend on the visibility of the other tagged type, but it does depend on the existence of the other tagged type. The other tagged type could have the default external tag or a user-specified external tag. Note that this means that a tagged type declared in a generic body that has user-defined external tag can only be instantiated once without raising Program_Error. Add after 13.3(76): Implementation Permissions If a user-specified external tag S'External_Tag is the same as T'External_Tag for some other tagged type declared in the partition, the partition may be rejected. AARM Ramification: This is, in general, a post-compilation check. This permission is intended for implementations that do link-time construction of the external tag lookup table; implementations that dynamically construct the table will likely prefer to raise Program_Error upon elaboration of the problem construct. We don't want this check to cause any implementation complexity, as it will be very rare that there would be a problem. !discussion We make the specification of the tag erroneous so that an implementation can raise an exception should it be able to detect the problem. Requiring a runtime check would require some sort of runtime table to check External_Tag names; the table would need exclusion in case multiple tasks are creating tagged types at the same time. That seems ike unnecessary overhead for a very unlikely case. A post-compilation check is not possible in general, as tagged types can be created in subprograms and tasks, and it is possible that the two types with conflicting external tags never exist at the same time. Such a program is *not* erroneous. [Or do we want to go further and ban this, too?? Seems like a slippery slope... - ED] Note that for default external tags, the implementation needs to prevent conflicts from happening (as noted in 13.3(76)). However, generic instance bodies are excepted from this requirement, and thus we need to worry about that case, as well. Internal_Tag is already covered, as it is defined to return "*a* tag" (that was intended to cover recursion, but it works here, too). So we just need a rule for Dependent_Tag. We made it a Bounded Error to minimize disruption but still to allow detection. For (2), we change the wording to "a master". The original intent was that master of the innermost task was "the" master, but that seems wrong (it would mean that the return of library-level tags would not be advised, which seems wrong). We could try to say that a tag whose master is the innermost one surrounding the call that has an applicable type is returned, but that is hard to word (or explain) and seems like overspecification, as it not likely that there are multiple applicable tagged types in different masters surrounding the call. It is enough to say that the tag should belong to some master of the call, especially as the primary goal is to prevent returning tags of types that belong to unrelated tasks. The wording "if one exists" is added to make it clear that this does not specify the result in all cases. Note that this change has the nice effect of matching the wording of the Implementation Permission 3.9(26/2), so that Tag_Error can be raised if the Implementation Advice cannot be followed. --!corrigendum 13.3(76) !ACATS Test !appendix !topic External tag clashes !reference AI95-00260, 13.3(73-76), 3.9(12.1) !from Adam Beneschan 08-06-13 !discussion It's occurred to me that if users specify values for 'External_Tags, it's possible that the external tags for two different type declarations may be the same, probably by accident. (Note: I'm using the term "type declaration" here because I don't want to discuss the case where distinct types are elaborated by the same type declaration, in a recursive routine or a task type, e.g. I think the semantics in that case are probably fine. My terminology may not be precisely correct here, but hopefully I'm making myself understood.) For distinct type declarations, the language requires that the *default* external tags be distinct (outside of generic instances). But AI-260 raises the possibility that a programmer may want to specify human-readable external tags for reading/writing XML files using streams. And it is conceivable that two different programmers working on different packages, or even the same programmer on a bad day, may decide to use the same 'External_Tag for different types. (Also possible, but probably less likely, a programmer may choose for an external tag a value that the implementation has chosen as the default 'External_Tag for some other type.) This could lead to some obvious problems---the tag in the XML file will be ambiguous, and very bad things could happen when it's read back in by an Ada program. One thing I noticed is that, while 3.9(12) has been changed from "Internal_Tag returns the tag" to "Internal_Tag returns a tag", 3.9(12.1) is worded in a way that assumes that there will be only one type corresponding to an external tag: "The function Descendant_Tag returns *the* (internal) tag for *the* type..." Since it's possible that more than one type may correspond to an external tag, this paragraph should probably say something about the possibility ("If more than one such type meets the requirements, the function result is the tag for an arbitrary such type"). But what I really think is necessary, that I don't see in the RM, is something like an Implementation Permission that if it is determined at runtime that two distinct type declarations have the same external tag (other than in two instances of the same generic, or maybe other cases where the tags are expected to be the same), Program_Error may be raised as soon as the situation is detected. I'm not convinced that there's anything in the RM that currently allows this behavior. **************************************************************** From: Thomas Quinot Date: Monday, June 16, 2008 12:54 PM I'd simply make that erroneous. **************************************************************** From: Adam Beneschan Date: Tuesday, June 17, 2008 10:23 AM That might work; if it's decreed that a program that has the same External_Tag for two distinct types (outside of instances) is erroneous, then any behavior, including raising an exception during libary package elaboration, is allowed. (So is erasing the hard disk, but I doubt that would actually happen.) I just wanted to mention that I don't think it's enough to say that programs with duplicate External_Tags are erroneous if they use Ada.Tags.Internal_Tag or Descendant_Tag. Clearly, if Internal_Tag or Descendant_Tag is called with an external tag and two or more distinct types have that same tag, the functions could return the wrong tag and serious havoc could result (particularly if Descendant_Tag is called from T'Class'Input, or if the resulting tag is used as a parameter to Generic_Dispatching_Constructor). But even a program that doesn't use either of those routines could cause problems. For example, a program that uses T'Class'Output (e.g. for writing an XML file) that thinks the external tag for two different types is the same, could end up writing a useless output file, which could later cause havoc for another program (even a non-Ada program) that tries to read it. That's why I think an error like this should be caught early. But whether this is done by an Implementation Permission or by stating that such programs are erroneous, I don't know. I'll leave it to others to decide what the best way of dealing with this is, if they agree that it needs to be dealt with. **************************************************************** !topic Probable wording error in 3.9(26.1) !reference 3.9(26.1) !from Adam Beneschan 08-06-13 !discussion This clause reads: "Internal_Tag should return the tag of a type whose innermost master is the master of the point of the function call". ^^^ I suspect that the word "the" right before "master" is wrong and should be "a". In 3.9(26), which uses almost identical wording, the word "a" is used: "... or if there is no such type whose innermost master is a master of the point of the function call". ^^^ But the wording "*the* master of the point of the function call" implies that, at the point where the function call occurs, there is one master that would be considered "the" master of that point. What would it be? It wouldn't make sense for it to be the innermost master, since that would in many cases be the simple_statement that performs the function call, or the function call itself; and that can't be the innermost master of the type. Even if "the" master were the innermost enclosing subprogram, the Implementation Advice would still not be followable in a case like: task body Task_Type_1 is type T1 is tagged record ... end record; for T1'External_Tag use "ext_T1"; procedure Inner is T : Ada.Tags.Tag; begin T := Ada.Tags.Internal_Tag ("ext_T1"); end; I'm guessing that this is just a typo in 3.9(26.1). **************************************************************** From: Randy Brukardt Sent: Friday, June 13, 2008 8:03 PM I'm certain it is not a typo (it's just wrong). For one thing, it was written before statements and calls and the like became masters. My intent (best as I recall it) was that the advice would suggest that Internal_Tag to only support library-level tagged types (since it has no practical way to find out which nested things are safe). Thus the wording was written to be as restrictive as possible. But of course it is wrong because it doesn't take the very inner masters into account -- really all we want to require is the *outermost* master. The bug is that there isn't a matching permission to raise an exception, so it isn't clear what to do in the cases that have the wrong master but aren't covered by permission. That probably happened because the ARG made me weaken the permission, and we forgot to change the advice. So you're probably right about the needed change, (given that I don't think the rest of the ARG would agree to a more restrictive rule), but it surely isn't a typo. (It also doesn't matter much either way, since the wording is Implementation Advice, which can always be ignored if needed.) ****************************************************************