!standard 4.3.2 (05) 03-05-22 AI95-00306/03 !class binding interpretation 02-08-28 !status Amendment 200Y 03-02-18 !status WG9 approved 03-06-20 !status ARG Approved 7-0-1 03-02-09 !status work item 02-10-09 !status received 02-08-07 !qualifier Error !priority Low !difficulty Medium !subject Class-wide extension aggregate expressions !summary If the ancestor_part of an aggregate is an expression, the expression must not be dynamically tagged. !question Is the aggregate in the following example illegal? (Yes.) package Foo is type T is tagged null record; function F (X : T) return T; type TT is new T with null record; function F (X : TT) return TT; end Foo; package body Foo is function F (X : T) return T is begin return X; end F; function F (X : TT) return TT is begin return X; end F; procedure P (X : in T'Class) is V : TT := (F (X) with null record); -- Error? (Yes.) begin null; end P; end Foo; This appears to be legal, because the type of the ancestor expression is T, and the aggregate's type (TT) is derived from T (through one or more record extensions...). This is true in spite of the fact that F (X) is a dispatching call. It seems this should be illegal, as it would require the expression object of T'Class (which might be an object of TT) to implicitly "truncate" to type T. Usually, this is illegal by 3.9.2(9), but that rule doesn't apply in this case because the expected type is not a specific tagged type. (4.3.2(4) specifies "some non-limited tagged type".) !recommendation (See summary.) !wording Add the following rule as the second sentence of 4.3.2(5): If the ancestor_part is an expression, it shall not be dynamically tagged. !discussion The current rules for resolution of an ancestor expression in an extension aggregate permit the expression to be dynamically tagged. This is because the name resolution rule in 4.3.2(4) specifies that the expression can be of any type in the class of nonlimited tagged types, and the restriction in 3.9.2(9/1) that disallows dynamically tagged expressions only applies if "the expected type for an expression or name is some specific tagged type". If an ancestor expression for an aggregate of type TT is a dispatching call of the form F (X), returning a specific type T that is an ancestor of type TT, then it will satisfy the resolution rule in 4.3.2(4) as well as the derivation rule of 4.3.2(5). It's clearly undesirable to allow a dispatching call as an ancestor expression, since logically the result of such a call can be of any type in the class. Even if the semantics were specified to involve a conversion to the specific root type, although this would be well-defined, it would be error-prone and probably not reflect the intent of the programmer (it's more likely to indicate a coding mistake). One approach to fixing the rules is to try and make a change that allows the restriction of 3.9.2(9/1) to apply. The simplest change seems to be to revise 4.3.2(4) to require an expression of any specific nonlimited tagged type rather than any nonlimited tagged type. The problem is that the rule in 3.9.2(9/1) is stated to only apply if "the expected type is some specific tagged type". However the wording in 4.3.2(4) talks about expecting a type in a class of types, and that doesn't seem to accord with expecting some specific type. It's not clear how 3.9.2(9/1) could be cleanly reworded to cover this case. Thus, this approach was rejected. Therefore, we add an additional legality rule to 4.3.2(5). !corrigendum 4.3.2(5) @drepl If the @fa is a @fa, it shall denote a specific tagged subtype. The type of the @fa shall be derived from the type of the @fa, through one or more record extensions (and no private extensions). @dby If the @fa is a @fa, it shall denote a specific tagged subtype. If the @fa is an @fa, it shall not be dynamically tagged. The type of the @fa shall be derived from the type of the @fa, through one or more record extensions (and no private extensions). !ACATS test A B-Test should be created to check that this rule is checked. !appendix From: Gary Dismukes Sent: Wednesday, August 7, 2002 2:58 PM One of our developers ran across a problem involving extension aggregates that raised a semantic issue. The question is whether the aggregate in the following example is illegal: package Foo is type T is tagged null record; function F (X : T) return T; type TT is new T with null record; function F (X : TT) return TT; end Foo; package body Foo is function F (X : T) return T is begin return X; end F; function F (X : TT) return TT is begin return X; end F; procedure P (X : in T'Class) is V : TT := (F (X) with null record); -- Error? begin null; end P; end Foo; By my reading of the rules this is legal, because the type of the ancestor expression is T, and the aggregate's type (TT) is derived from T (through one or more record extensions...). So this appears to be legal in spite of the fact that F (X) is a dispatching call. It seems that this should be illegal, but unless I'm missing something the RM doesn't disallow it. If it's agreed that it's currently legal, then I think that it's desirable to open an AI that would disallow it. Even if the dynamic semantics of what such an aggregate means are well defined (though actually I think they're not), this construct seems harmful to allow since it's almost certainly not what the programmer intended to write. What do others think? **************************************************************** From: Robert Dewar Sent: Wednesday, August 7, 2002 4:04 PM I definitely agree this example should be illegal. In fact I think it *is* illegal on the grounds that the RM does not say silly things :-) :-) **************************************************************** From: Bob Duff Sent: Wednesday, August 7, 2002 3:36 PM I think it's illegal by RM-3.9.2(9/1). F(X) is dynamically tagged, but it's not a controlling operand. **************************************************************** From: Gary Dismukes Sent: Wednesday, August 7, 2002 4:16 PM I had looked at that rule, but as far as I can see it doesn't apply in this case, because the expected type is not "some specific tagged type". The ancestor expression is "expected to be of any nonlimited tagged type". **************************************************************** From: Robert Eachus Sent: Saturday, August 10, 2002 10:36 AM For me the interesting question is whether: V: TT := TT'((F(X) with null record)); -- Legal? ...is legal. As far as I can see, it is. Certainly: Y: T := (F(X) with null record); V: TT := TT(Y); ...is illegal. So the question is whether an implicit type conversion should be allowed in this situation. My feeling is that it should be explicit, since it is very unclear otherwise whether the user is expecting TT'(F(X) with null record) or TT(F(X) with null record). Yeah, I know that most users have a hard time telling which one they really did mean, but at least we can refuse to hide the choice. (Yes, I know that the choices as stated may not be technically legal, take it as a placeholder for making the case under discussion legal.) Incidently GNAT seems to have real problems with this case and variants on it. I won't submit a bug report until I know what the right result is, but one problem seems to be an indefinite loop in type resolution.. **************************************************************** From: Gary Dismukes Sent: Wednesday, October 9, 2002 8:53 PM This is a draft of AI-306. Two possible wording changes are discussed, so after we agree on the right fix it should be possible to condense the !wording and !discussion sections. (* Editor's note: This is version 01 of the AI. *) **************************************************************** From: Tucker Taft Sent: Wednesday, October 9, 2002 9:52 PM I prefer one new simple rule to adding complexity to an already complex rule. Hence, I prefer solution 1. **************************************************************** From: Robert A. Duff Sent: Thursday, October 10, 2002 12:19 PM > First solution: > > Add the following rule as the second sentence of 4.3.2(5): > > {If the ancestor_part is an expression, it shall not be dynamically tagged.} I like that. > The above sentence definitely seems to fix the problem, but the following > change would avoid adding a new rule: > > Second solution: > > Revise the second sentence of 4.3.2(4) to read: > > If the ancestor_part is an expression, it is expected to be of any {specific} > nonlimited tagged type. > > However, it's not clear that the second proposed solution will work, > as the "expected to be of any ... type" wording still seems to prevent > 3.9.2(9/1) from applying. See discussion. Whether it works or not, it seems undesirable to me. I don't like making the Name Resolution rules too precise. E.g, if there's a function F that return T'Class, and a different F that returns T, I don't want the compiler to pick one of them -- I want it to be ambiguous. This rule is already too precise, IMHO. I would have left out the part about "nonlimited" and "tagged", which only serve to aid in the writing of obscure code. Adding "specific" just makes it worse. So I prefer the First Solution. ****************************************************************