!standard 3.9.3(7) 20-12-03 AI12-0413-1/01 !standard 4.5.2(14.1/3) !standard 4.5.2(24.1/3) !standard 12.5(8/3) !class binding interpretation 20-12-03 !status work item 20-12-03 !status received 20-11-28 !priority Low !difficulty Easy !qualifier Omission !subject Reemergence of "=" when defined to be abstract !summary User-defined primitive equality of an untagged record type hides the predefined equality operator, including within a generic, and if it is declared abstract, results in an illegal instance if the operator is used in the spec, or in the raising of Program_Error if used in the body of an instance. !question There seems to be no rule to prevent the following from compiling: procedure Test_Inst is generic type Formal_T is private; with function F (L, R : Formal_T) return Boolean is "="; package Gen is end Gen; package body Gen is I, J : Formal_T; B : Boolean := I = J; C : Boolean := F(I, J); end Gen; type T is range 1..10; function "=" (L, R : T) return Boolean is abstract; package Instance is new Gen (T); begin null; end Test_Inst; However, this seems to allow calling an abstract function. Should the above be illegal, or raise Program_Error? (Neither; if T is an elementary type it calls the predefined "=".) What happens if we change "T" to be a record type? (Program_Error is raised.) !recommendation Whether the above silently uses the predefined "=" or raises Program_Error depends on whether the actual type is or is not a record type. Since the actual "T" is not a record type, the predefined "=" is used, as explained in RM 12.5(8/3). This is the "usual" reemergence of predefined operators that occurs for elementary types. On the other hand, if you changed the example to make the actual T into a record type, then the user-defined "=" hides completely the predefined "=", including in generics and when appearing as a component in a composite equality. Alas, this is not completely clear in the current wording of 12.5(8/3). It is implied to some degree by RM 4.5.2(14.1/3), but that only applies to derived types. So we propose to update RM 12.5(8/3) below, and remove RM 4.5.2(14.1/3) as it no longer serves a purpose. Replace 3.9.3(7): A call on an abstract subprogram shall be a dispatching call; [Redundant: nondispatching calls to an abstract subprogram are not allowed.] with: A call on an abstract subprogram shall be a dispatching call; nondispatching calls to an abstract subprogram are not allowed. In addition to the places where Legality Rules normally apply (see 12.3), these rules also apply in the private part of an instance of a generic unit. Modify AARM 3.9.3(7.a/2): Ramification: If an abstract subprogram is not a dispatching operation of some tagged type, then it cannot be called at all. In Ada 2005, such subprograms are not even considered by name resolution (see 6.4). {However, this rule is still needed for cases that can arise in the instance of a generic specification where Name Resolution rules are not reapplied, but Legality Rules are, when the equality operator for an untagged record type is abstract, while the operator for the formal type is not abstract (see 12.5).} Delete RM 4.5.2(14.1/3), which was: For a derived type whose parent is an untagged record type, predefined equality is defined in terms of the primitive (possibly user-defined) equals operator of the parent type. And also delete AARM 4.5.2(14.e/3), which was: Reason: This prevents predefined equality from reemerging in generic units for untagged record types. For other uses the primitive equality is inherited and the inherited routine is primitive. Modify RM 4.5.2(24.1/3): If the primitive equals operator for an untagged record type is abstract, then Program_Error is raised at the point of any [(implicit)] call to that abstract subprogram{[Redundant:, implicitly as part of an equality operation on an enclosing composite object, or in an instance of a generic with a formal private type where the actual type is a record type with an abstract "="]}. Modify AARM 4.5.2(24.f/3): Reason: An explicit call to an abstract subprogram is {generally} illegal. This rule is needed in order to define the effect of {a call in an instance of a generic body, or} an implicit call such as a call that is part of the predefined equality operation for an enclosing composite type that has a component of an untagged record type that has an abstract primitive equals operator. For tagged types, an abstract primitive equals operator is only allowed for an abstract type, and abstract types cannot be components, so this case does not occur. Modify RM 12.5(8/3): [Redundant: The formal type also belongs to each category that contains the determined category.] The primitive subprograms of the type are as for any type in the determined category. For a formal type other than a formal derived type, these are the predefined operators of the type. For an elementary formal type, the predefined operators are implicitly declared immediately after the declaration of the formal type. For a composite formal type, the predefined operators are implicitly declared either immediately after the declaration of the formal type, or later immediately within the declarative region in which the type is declared according to the rules of 7.3.1. In an instance, the copy of such an implicit declaration declares a view of the predefined operator of the actual type, even if this operator has been overridden for the actual type and even if it is never declared for the actual type{, unless the actual type is an untagged record type, in which case it declares a view of the primitive (equality) operator}. [Redundant: The rules specific to formal derived types are given in 12.5.1.] Add after RM 12.5(8/3): AARM Ramification: If the primitive equality operator of the (actual) untagged record type is declared abstract, then Program_Error will be raised if the equality operator of the formal type is in fact invoked within an instance of a generic body (see 4.5.2(24.1/3)). If the operator is invoked within an instance of the generic spec, the instance is illegal. !discussion The rule given in RM 4.5.2(14.1/3), which we propose to delete, is followed by an AARM note (which we also propose to delete) which implies that it solves the problem that can arise in an instance of a generic, but it only solves the problem for derived types. The AARM "Reason" given implies that the statement in paragraph 14.1/3 about derived types is adequate to prevent reemergence of predefined "=" for an untagged record type, but it doesn't say anything about non-derived types, nor does it deal properly with the case where the derived type has its own user-defined "=" routine. The original "(implicit)" in 4.5.2(24.1/3) no longer recognizes all of the cases where Program_Error can arise, so we remove "(implicitly)" and replace it with some "redundant" wording that explains the two cases where Program_Errors can arise. !example Here we reuse the example from the !question, with a minor edit: procedure Test_Inst is generic type Formal_T is private; with function F (L, R : Formal_T) return Boolean is "="; package Gen is I, J : Formal_T; A : Boolean := I = J; -- Illegal or Program_Error? end Gen; package body Gen is B : Boolean := I = J; -- Illegal or Program_Error? C : Boolean := F(I, J); -- Illegal or Program_Error? end Gen; type T is range 1..10; function "=" (L, R : T) return Boolean is abstract; package Instance is new Gen (T); -- A, B, C are all legal, and use the predefined "=" type U is null record; function "=" (L, R : U) return Boolean is abstract; package Instance is new Gen (U); -- A is illegal; B and C are legal but would raise Program_Error begin null; end Test_Inst; --- In the above, the instantiation with the elementary type T is legal, and would use the predefined "=" in the instance for the initializations of A, B, and C. On the other hand, the instantiation with the elementary type U is illegal due to the use of the abstract "=" in the instance of the generic spec, but if we remove the declaration of A, then it would be legal, but raise Program_Error at the use of "=" and its rename "F" in the instance of the generic body. !ASIS No ASIS effect. !ACATS test Existing ACATS tests check the basic rules for equality, particularly record equality. We could consider a C-Test to check that an abstract equality for an untagged record type raises Program_Error if used in a generic unit, but that is a fairly rare case so this is low priority test. !appendix From: Jean-Pierre Rosen Sent: Thursday, November 26, 2020 4:15 AM Another AI, if we miss stuff to discuss at the next meeting... [Attached] !standard <20-11-26> AI12-000X-1/01 !class !status work item !status received !priority Medium !difficulty Medium !qualifier !subject Calling an abstract "=" !summary !question There seems to be no rule to prevent the following from compiling: procedure Test_Inst is generic type Formal_T is private; with function F (L, R : Formal_T) return Boolean is "="; package Gen is end Gen; package body Gen is I, J : Formal_T; B : Boolean := I=J; end Gen; type T is range 1..10; function "=" (L, R : T) return Boolean is abstract; package Instance is new Gen (T); begin null; end Test_Inst; However, this allows calling an abstract function. Should it be forbidden, or raise Program_Error? !recommendation !wording !discussion I think that, at least until Ada 2012, this is correct, due to the reemergence of predefined equality, i.e. the default "=" is the one that exists for every formal private type, not the abstract one. However, I think that there is an AI in Ada 202X to fix that :#! reemergence, and that could be the origin of the problem. Not sure that there is wording in the RM for that case either. In a private discussion, Tucker said: After looking through the RM, it seems that the use of an abstract "=" in this context is not directly covered, but the closest thing is the rule about what happens when you wrap a type with an abstract "=" into a record or array type. The abstract "=" raises Program_Error if it is ever invoked. See RM 4.5.2 (24.1/3): 24.1/3 {AI05-0123-1} If the primitive equals operator for an untagged record type is abstract, then Program_Error is raised at the point of any (implicit) call to that abstract subprogram. 24.f/3 Reason: An explicit call to an abstract subprogram is illegal. This rule is needed in order to define the effect of an implicit call such as a call that is part of the predefined equality operation for an enclosing composite type that has a component of an untagged record type that has an abstract primitive equals operator. For tagged types, an abstract primitive equals operator is only allowed for an abstract type, and abstract types cannot be components, so this case does not occur. So my sense is that this should *not* be a compile-time error, but rather raise Program_Error if the "=" operator of the formal type is invoked. We also should write up an AI to at least clarify this particular case. We could try to make this illegal in the generic spec, but clearly we cannot make it illegal in the generic body, because of contract issues. But I think the same argument applies here, namely that there is no need to disallow this instantiation, unless "=" is actually used, which argues for raising Program_Error rather than making it illegal. !ASIS !ACATS test **************************************************************** From: Tucker Taft Sent: Thursday, November 26, 2020 11:52 AM > However, this allows calling an abstract function. Should it be > forbidden, or raise Program_Error? The answer depends on whether the actual type is or is not a record type. Since the actual "T" is not a record type, the predefined "=" is used, and there is nothing to worry about. On the other hand, if you changed the example to make the actual T into a record type, then the user-defined "=" hides completely the predefined "=", including in generics and when appearing as a component in a composite equality. Alas, this is not completely clear in the current wording. I believe this paragraph needs work. See the additional wording in {...}: RM 12.5(8/3): [Redundant: The formal type also belongs to each category that contains the determined category.] The primitive subprograms of the type are as for any type in the determined category. For a formal type other than a formal derived type, these are the predefined operators of the type. For an elementary formal type, the predefined operators are implicitly declared immediately after the declaration of the formal type. For a composite formal type, the predefined operators are implicitly declared either immediately after the declaration of the formal type, or later immediately within the declarative region in which the type is declared according to the rules of 7.3.1. In an instance, the copy of such an implicit declaration declares a view of the predefined operator of the actual type, even if this operator has been overridden for the actual type and even if it is never declared for the actual type{, unless the actual type is an untagged record type, in which case it declares a view of the primitive (equality) operator}. [Redundant: The rules specific to formal derived types are given in 12.5.1.] --- I think the addition is needed for untagged record types, because the "trick" used to execute the overriding for tagged record types doesn't work for untagged records, since there is no "controlling tag" to determine the body to execute. In RM 4.5.2, there is rule followed by an AARM note which implies that it solves the problem, but I don't think it does: RM 4.5.1(14.1/3): For a derived type whose parent is an untagged record type, predefined equality is defined in terms of the primitive (possibly user-defined) equals operator of the parent type. (14.e/3) Reason: This prevents predefined equality from reemerging in generic units for untagged record types. For other uses the primitive equality is inherited and the inherited routine is primitive. --- The "Reason" given above seems to imply that the statement about derived types is adequate to prevent reemergence of predefined "=" for an untagged record type, but it doesn't say anything about non-derived types, nor does it deal properly with the case where the derived type has its own user-defined "=" routine. To address the issue about Program_Error being raised if an abstract "=" is ever invoked, I would suggest we add an AARM note after the (modified) 12.5(8/3): AARM Ramification: If the primitive equality operator of the untagged record type is declared abstract, then Program_Error will be raised if the operator is actually invoked within the generic (see 4.5.2(24.1/3)). **************************************************************** From: Randy Brukardt Sent: Friday, November 27, 2020 10:17 PM ... > The answer depends on whether the actual type is or is not a record > type. Since the actual "T" is not a record type, the predefined "=" > is used, and there is nothing to worry about. Well, the user probably didn't expect anyone to be able to call the original "=". So there's definitely something to worry about! It just isn't a language hole. :-) Probably it would have been better to say that the "language semantics are clear, and no call to an abstract function is involved". ... > I think the addition is needed for untagged record types, because the > "trick" used to execute the overriding for tagged record types doesn't > work for untagged records, since there is no "controlling tag" to > determine the body to execute. Agreed. I do wonder if we should continue to depend on that "trick", since it would be confusing to do this *only* for untagged types. If we need a special rule anyway, shouldn't we just use it for any record types? BTW, is this right for a private type completed by a record type? Answering my own question, yes, because it redefines predefined equality in 4.5.2(15/5). > In RM 4.5.2, there is rule followed by an AARM note which implies that > it solves the problem, but I don't think it does: > > RM 4.5.1(14.1/3): > > For a derived type whose parent is an untagged record type, > predefined equality is defined in terms of the primitive (possibly > user-defined) equals operator of the parent type. > > (14.e/3) > Reason: This prevents predefined equality from reemerging in > generic units for untagged record types. For other uses the primitive > equality is inherited and the inherited routine is primitive. > > --- > > The "Reason" given above seems to imply that the statement about > derived types is adequate to prevent reemergence of predefined "=" for > an untagged record type, but it doesn't say anything about non-derived > types, nor does it deal properly with the case where the derived type > has its own user-defined "=" routine. That being the case, shouldn't we just get rid of this rule? It doesn't seem to serve any purpose given the change you are suggesting to 12.5(8/3). So far as I can tell from looking at AI05-0123-1, where this originated, it was intended to be a copy of the similar rule for type_extensions. But of course that rule has to add in extension components, and that seems to be the meaty part. > To address the issue about Program_Error being raised if an abstract > "=" is ever invoked, I would suggest we add an AARM note after the > (modified) 12.5(8/3): > > AARM Ramification: If the primitive equality operator of the > untagged record type is declared abstract, then Program_Error will be > raised if the operator is actually invoked within the generic (see > 4.5.2(24.1/3)). The "(implicit)" in 4.5.2(24.1/3) is a lie if we go this way - it seems to be referring to a call that is part of some enclosing equality, whereas in a generic it is a direct invocation (just one in a generic body where Legality Rules aren't enforced). Probably at a minimum the AARM notes ought to show this possibility. Also, with your rewording of 12.5(8/3), one would need a recheck in the specification of an instance; any call on an abstract "=" is illegal (and thus makes the instance illegal). This might be a surprise to implementers! **************************************************************** From: Tucker Taft Sent: Saturday, November 28, 2020 9:03 PM > Well, the user probably didn't expect anyone to be able to call the original > "=". So there's definitely something to worry about! It just isn't a > language hole. :-) Probably it would have been better to say that the > "language semantics are clear, and no call to an abstract function is > involved". Fair enough. ... >That being the case, shouldn't we just get rid of this rule? It doesn't seem >to serve any purpose given the change you are suggesting to 12.5(8/3). Agreed. >So far as I can tell from looking at AI05-0123-1, where this originated, it >was intended to be a copy of the similar rule for type_extensions. But of >course that rule has to add in extension components, and that seems to be >the meaty part. Agreed. >The "(implicit)" in 4.5.2(24.1/3) is a lie if we go this way - it seems to >be referring to a call that is part of some enclosing equality, whereas in a >generic it is a direct invocation (just one in a generic body where Legality >Rules aren't enforced). Probably at a minimum the AARM notes ought to show >this possibility. Agreed. >Also, with your rewording of 12.5(8/3), one would need a recheck in the >specification of an instance; any call on an abstract "=" is illegal (and >thus makes the instance illegal). This might be a surprise to implementers! Agreed, though I don't think it would be much of a stretch for implementations that do macro expansion. **************************************************************** From: Tucker Taft Sent: Thursday, December 3, 2020 10:58 AM Here is the AI coming out of this question. [This is version /01 of the AI - with the fix below - Editor.] **************************************************************** From: Arnaud Charlet Sent: Thursday, December 3, 2020 11:21 AM > !summary Looks like you put the summary as part of the !question (and answers) part. I'd suggest moving the interesting part in !summary to ease reading. **************************************************************** From: Tucker Taft Sent: Thursday, December 3, 2020 1:02 PM Oops, right you are. Should have been: !summary User-defined primitive equality of an untagged record type hides the predefined equality operator, including within a generic, and if it is declared abstract, results in an illegal instance if the operator is used in the spec, or in the raising of Program_Error if used in the body of an instance. **************************************************************** From: Randy Brukardt Sent: Thursday, December 3, 2020 11:53 PM ... > Modify RM 12.5(8/3): > > [Redundant: The formal type also belongs to each category that > contains the determined category.] The primitive subprograms of the > type are as for any type in the determined category. For a formal type > other than a formal derived type, these are the predefined operators > of the type. For an elementary formal type, the predefined operators > are implicitly declared immediately after the declaration of the > formal type. For a composite formal type, the predefined operators are > implicitly declared either immediately after the declaration of the > formal type, or later immediately within the declarative region in > which the type is declared according to the rules of 7.3.1. In an > instance, the copy of such an implicit declaration declares a view of > the predefined operator of the actual type, even if this operator has > been overridden for the actual type and even if it is never declared > for the actual type{, unless the actual type is an untagged record > type, in which case it declares a view of the primitive (equality) > operator}. [Redundant: The rules specific to formal derived types are > given in 12.5.1.] I would like an AARM note explaining why we only need a special case for untagged record types, given that the rules are essentially the same for all record types. I realize that someone can tease it out by reading 4.5.2 carefully, but this is a Chapter 12 paragraph. Why be confused? (I'm not proposing one because *I'm* still confused. :-) Alternatively, of course, we could have the rules the same for all record types. That would be preferable, but I'm unsure if it causes any problems. Steve?? ****************************************************************