Version 1.3 of ai12s/ai12-0413-1.txt

Unformatted version of ai12s/ai12-0413-1.txt version 1.3
Other versions for file ai12s/ai12-0413-1.txt

!standard 3.9.3(7)          20-12-17 AI12-0413-1/03
!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 Amendment 1-2012 20-12-11
!status ARG Approved 15-0-0 20-12-09
!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; because T is an elementary type it calls the predefined "=".)
!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). If the operator is invoked within an instance of the generic spec, the instance is illegal.
!discussion
A different answer would have been given had the type T in the question been a record type. In that case, the primitive "=" is used in the generic, and 4.5.2(24.1/3) would apply, so Program_Error would be raised. As mentioned in the new AARM note in 12.5, any such equality operators in a generic specification are rechecked in an instance, making the instance illegal.
Had the type T in the question been an array type, the answer would be the same as for a scalar type - the predefined "=" would reimerge and the abstract "=" would be ignored.
The original "(implicit)" in 4.5.2(24.1/3) does not recognize all of the cases where Program_Error can arise (the generic case was apparently not considered), so we remove "(implicit)" and replace it with some "redundant" wording that explains the two cases where Program_Errors can arise.
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. Once we fix those cases, the fix covers this case as well and there is no other need for this rule.
!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.
!corrigendum 3.9.3(7)
Replace the paragraph:
A call on an abstract subprogram shall be a dispatching call; nondispatching calls to an abstract subprogram are not allowed.
by:
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.
!corrigendum 4.5.2(14.1/3)
Delete the paragraph:
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.
!corrigendum 4.5.2(24.1/3)
Replace the paragraph:
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.
by:
If the primitive equals operator for an untagged record type is abstract, then Program_Error is raised at the point of any call to that abstract subprogram, 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 "=".
!corrigendum 12.5(8/3)
Replace the paragraph:
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. The rules specific to formal derived types are given in 12.5.1.
by:
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. The rules specific to formal derived types are given in 12.5.1.
!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 <RM paragraphs>                          <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??

****************************************************************


Questions? Ask the ACAA Technical Agent