!standard 3.9(12.1/2) 10-04-02 AI05-0113-1/05 !standard 3.9(26.1/2) !standard 13.3(76) !class binding interpretation 08-10-06 !status ARG Approved 6-0-3 10-02-27 !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 declaration, Program_Error is raised by the elaboration of S. 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 Modify 3.9(12.1/2): The function Descendant_Tag returns the (internal) tag for the type that corresponds to the given external tag and is both a descendant of the type identified by the Ancestor tag and has the same accessibility level as the identified ancestor. Tag_Error is raised if External is not the external tag for such a type. Tag_Error is also raised if the specific type identified is a library-level type whose tag has not yet been created{, or if the given external tag identifies more than one type that has the appropriate Ancestor and accessibility level}. AARM Note: Rules for specifying external tags will usually prevent an external tag from identifying more than one type. However, an external tag can identify multiple types 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 as the type. (There is an exception to the general requirement that default external tags be unique for this case.) 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 by a different declaration 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. This rule allows the same declaration to be elaborated multiple times. In that case, different types could have the same external tag. If that happens, Internal_Tag would return some unspecified tag, and Decendant_Tag probably would return the intended tag (using the given ancestor to determine which type is intended). However, in some cases (such as multiple instantiations of a derived tagged type declared in a generic body), Tag_Error might be raised by Descendant_Tag if multiple types are identified. Note that while there is a race condition inherent in this definition (which attribute_definition_clause raises Program_Error depends on the order of elaboration), it doesn't matter as a program with two such clauses is simply wrong. Two types that both come from the same declaration are allowed, as noted previously. 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 by a different declaration 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 require any implementation complexity, as it will be very rare that there would be a problem. !discussion The runtime check will require some sort of runtime table to check External_Tag names; the table will need exclusion in case multiple tasks are creating tagged types at the same time. The mechanism used to implement Internal_Tag should be sufficient. 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. We do allow compilers to reject programs that probably will fail this check at runtime. 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 does not need a rule, as it is defined to return "*a* tag" (that was intended to cover recursion, but it works here, too). But we do need a rule for Dependent_Tag. We made it raise Tag_Error in that case to use the same exception as it does other problems. One ramification of these rules is that if an external tag is specified for a type declared within a generic unit, the generic can only be successfully instantiated once. (Two generic instances are "different declarations"; the second instance to be elaborated must raise Program_Error.) We believe it would be better to allow the external name to depend on formal parameters (which would mean dropping the static string requirement) than to intentionally have conflicting tag names. 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.) ****************************************************************