Version 1.4 of ais/ai-00294.txt

Unformatted version of ais/ai-00294.txt version 1.4
Other versions for file ais/ai-00294.txt

!standard 3.9.3 (09)          02-05-23 AI95-00294/03
!class ramification 03-05-23
!status work item 02-05-16
!status received 02-05-13
!qualifier Clarification
!priority Low
!difficulty Medium
!subject Instantiating with abstract operations
!summary
It is illegal to instantiate a generic which has an abstract formal derived type parameter that inherits a concrete operation with a type that provides an abstract version of the concrete operation.
!question
Consider the following example:
procedure Foo is package P1 is type T1 is abstract tagged null record; procedure P (X : T1); end P1;
package body P1 is procedure P (X : T1) is
begin Text_Io.Put_Line ("P1.P called");
end P; end P1;
generic type D is abstract new P1.T1 with private; procedure G (X : D);
procedure G (X : D) is begin
P (X); end G;
package P2 is type T2 is abstract new P1.T1 with null record; procedure P (X : T2) is abstract; end P2;
procedure I is new G (P2.T2);
package P3 is type T3 is new P2.T2 with null record; procedure P (X : T3); end P3;
package body P3 is procedure P (X : T3) is
begin Text_Io.Put_Line ("P3.P called");
end P; end P3;
begin declare X : P3.T3; begin I (P2.T2 (X)); end; end Foo;
The test passes the check of 3.9.3(9), sentence 2:
If a generic formal type is abstract, then for each primitive subprogram of the formal that is not abstract, the corresponding primitive subprogram of the actual shall not be abstract.
because the corresponding subprogram in this case is not itself abstract (although it is overridden by an abstract subprogram).
Thus, the test is not rejected at compile time.
What happens at runtime when Foo.I is called? It appears that the abstract body will be called.
!response
3.9.3(9) indeed covers this case. We are not in an instance of the generic at the point of the instantiation, so 12.5.1(21/1) does not apply. Moreover, the standard uses 'corresponding' in its English sense (it is never defined as a technical term anywhere in the standard). Thus, the instantiation is illegal.
This is as it should be. The fact that the operation P is not abstract is part of the contract of the generic unit. As always in Ada, an instantiation that does not meet its contract is rejected. This is true even if P is not called in the body of the generic. We don't allow instantiating a generic with an integer type parameter with Boolean as long as "+" and the other mathematics operators aren't called, so this case should not be any different.
In order to clarify the wording, we add "(nonoverridden)" to 3.9.3(9).
!corrigendum 3.9.3(09)
!drepl
If a partial view is not abstract, the corresponding full view shall not be abstract. If a generic formal type is abstract, then for each primitive subprogram of the formal that is not abstract, the corresponding primitive subprogram of the actual shall not be abstract.
!dby
If a partial view is not abstract, the corresponding full view shall not be abstract. If a generic formal type is abstract, then for each primitive subprogram of the formal that is not abstract, the corresponding (nonoverridden) primitive subprogram of the actual shall not be abstract.
!ACATS test
A B-Test could be created for this case, but I don't know how valuable it would be.
!appendix

From: Steve Baird
Sent: Monday, May 13, 2002  5:35 PM

Consider the following example:

  procedure Foo is
    package P1 is
	type T1 is abstract tagged null record;
	procedure P (X : T1);
    end P1;
    package body P1 is
	procedure P (X : T1) is
	begin
	    Text_Io.Put_Line ("P1.P called");
	end P;
    end P1;

    generic
	type D is abstract new P1.T1 with private;
    procedure G (X : D);
    procedure G (X : D) is
    begin
	P (X);
    end G;

    package P2 is
	type T2 is abstract new P1.T1 with null record;
	procedure P (X : T2) is abstract;
    end P2;

    procedure I is new G (P2.T2);

    package P3 is
	type T3 is new P2.T2 with null record;
	procedure P (X : T3);
    end P3;
    package body P3 is
	procedure P (X : T3) is
	begin
	    Text_Io.Put_Line ("P3.P called");
	end P;
    end P3;

  begin
    declare
	X : P3.T3;
    begin
	I (P2.T2 (X));
    end;
  end Foo;

The test passes the check of 3.9.3(9), sentence 2:

   If a generic formal type is abstract, then
   for each primitive subprogram of the formal that
   is not abstract, the corresponding primitive subprogram
   of the actual shall not be abstract.

because the corresponding subprogram in this case is
not itself abstract (although it is overridden by an
abstract subprogram).

Thus, the test is not rejected at compile time.

What happens at runtime when Foo.I is called?

12.5.1(21/1) states that " ... if the tag in the call is statically
determined to be that of the formal type, the body executed will be
that corresponding to the actual type".

The wording here might seem odd, but the meaning is clear: the body
executed will be that of the corresponding subprogram of the actual type.

By 3.9.2(20),

   The body for an implicitly declared operation that is overridden
   is the body for the overriding subprogram.

, this is the "body" of the explicitly declared abstract subprogram P2.P.

In the case where an abstract subprogram overrides a non-abstract
subprogram, perhaps the body of the overrider should be defined to
be that of the overridden? Or perhaps Program_Error should be raised
in this scenario?

This is another case where the "assume the worst in a generic body"
approach would be too restrictive. Conservatively rejecting the
call to P in the body of G would not be good.

Again, I do not see an obvious solution.

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

From: Christoph Grein
Sent: Tuesday, May 14, 2002  3:36 AM

>   procedure Foo is
>     package P1 is
> 	type T1 is abstract tagged null record;
> 	procedure P (X : T1);
>     end P1;
>     package body P1 is
> 	procedure P (X : T1) is
> 	begin
> 	    Text_Io.Put_Line ("P1.P called");
> 	end P;
>     end P1;
>
>     generic
> 	type D is abstract new P1.T1 with private;

        -- The primitive formal P (X : D) is not abstract here

>     procedure G (X : D);
>     procedure G (X : D) is
>     begin
> 	P (X);
>     end G;
>
>     package P2 is
> 	type T2 is abstract new P1.T1 with null record;
> 	procedure P (X : T2) is abstract;
>     end P2;
>
>     procedure I is new G (P2.T2);

      -- The primitive actual P (X : T2) is abstract here

>
>...
>
> The test passes the check of 3.9.3(9), sentence 2:
>
>    If a generic formal type [D] is abstract, then
>    for each primitive subprogram of the formal that
>    is not abstract [P (X: D)], the corresponding primitive subprogram
>    [P2.P(X:T2)] of the actual [P2.T2] shall not be abstract.

[insertions by me]

>
> because the corresponding subprogram in this case is
> not itself abstract (although it is overridden by an
> abstract subprogram).
>

Thus, for me the instantiation clearly violates 3.9.3(9).
Not being a language lawyer, I do not see why this should compile and be legal.

When I is called, the abstract subprogram P2.P(X:T2) should be called, which
does not have a body!

I'm confused...

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

From: Pascal Leroy
Sent: Tuesday, May 14, 2002  3:53 AM

No, you are misinterpreting the word "corresponding" here.  The second sentence
of RM95 12.5.1(21) states that "in an instance, the copy of such an implicit
declaration declares a view of the corresponding primitive subprogram of the
ancestor, even if this primitive has been overridden for the actual type."  So
the "corresponding primitive subprogram" in RM95 3.9.3(9) is the one that has
been overridden (and is not abstract), not the overrider (which is abstract).

In other words the instance knows about the original (non-abstract, overridden)
subprogram, but not about the new one (overrider, abstract).

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

From: Tucker Taft
Sent: Tuesday, May 14, 2002  2:33 PM

Huh???

We aren't in an instance at the point of the instantiation.  3.9.3(9) is
stating a rule that governs whether the instantiation is legal, and in
this case, the instantiation is clearly illegal, because the primitive
"P" is non-abstract for the formal type "D" and is abstract for the actual
type "P2.T2."

Perhaps the wording is somehow confusing, but I don't know what sort
of clarification would help because it is crystal clear to me!

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

From: Pascal Leroy
Sent: Tuesday, May 14, 2002  4:04 PM

It may be crystal-clear to you, but it is certainly not crystal-clear to me,
and as a matter of fact it's not what our compiler implements (there may be
a causal relationship here).

3.9.3(9) uses the word "corresponding", but it doesn't define it.  The
closest thing to a definition of "corresponding" that can be found in the RM
is 12.5.1(21), which precisely says that "corresponding" means "even if it
has been overridden".  Using "corresponding" without defining it is lousy
wording at its best.

And yes, I realize that we are not in the instance when we are checking the
legality of the instantiation, but that's irrelevant.

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

From: Tucker Taft
Sent: Tuesday, May 14, 2002  5:07 PM

"Corresponding" is used in section 3.4 in several places. It is also used in
6.3.1(13.1/1), and in fact it is used all over the manual, to mean the
"obvious" thing (I thought ;-).  In 12.5.1(21) we also *don't* define
"corresponding" but rather use it in defining which subprogram is copied.

In general, "corresponding" means with the same name (for components), or in
the same position (for discriminants or parameters), or with the same name and
"nearly" same profile (in the case of primitive subprograms).  In general, it
means "corresponding"! ;-).

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

From: Pascal Leroy
Sent: Wednesday, May 15, 2002  3:23 AM

Well, I am not going to take "Tuck said so" for an answer, and I am going to
ask the Rapporteur and the Editor to create an AI for this.

> In general, "corresponding" means with the same name (for components), or in
> the same position (for discriminants or parameters), or with the same name
> and "nearly" same profile (in the case of primitive subprograms). In
> general, it means "corresponding"! ;-).

I am sure that you realize that there is a lot of hand waving in "same name and
nearly same profile", especially as both the overridden subprogram and the
overrider qualify. So much hand waving that I can feel the wind from across the
ocean :->

> It seems relevant, because 12.5.1(21) is talking about what gets copied in
> an instance, and here we are deciding whether an instantiation is legal.

But then I am confused by the purpose of 3.9.3(9). It is clear from 12.5.1(21)
and AARM 12.5.1(21.a) that in the instance the name of the primitive subprogram
refers to the overridden one (among other things, you can tell the difference
based on the default parameters). So we have a perfectly good, non-abstract
subprogram to call. Why is it that we are disallowing the instantiation because
the actual happens to have an abstract subprogram, that is never going to be
referenced by the instantiation?

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

From: Tucker Taft
Sent: Wednesday, May 15, 2002  9:06 AM

> Well, I am not going to take "Tuck said so" for an answer, and I am going
> to ask the Rapporteur and the Editor to create an AI for this.

Fair enough!

> But then I am confused by the purpose of 3.9.3(9).  It is clear from
> 12.5.1(21) and AARM 12.5.1(21.a) that in the instance the name of the
> primitive subprogram refers to the overridden one (among other things, you
> can tell the difference based on the default parameters). So we have a
> perfectly good, non-abstract subprogram to call. Why is it that we are
> disallowing the instantiation because the actual happens to have an abstract
> subprogram, that is never going to be referenced by the instantiation?

It is true that it uses the formal parameter names and defaults from the
ancestor primitive (since that is all that it knows at generic-compile-time),
but it still invokes the body of the actual's primitive at run-time (see the
last sentence of 12.5.1(21/1)). So it would be bad news if this were abstract.

This is analagous to what happens when a primitive is overridden in the
private part of a package. The callers use the formal parameter names
and defaults of the inherited subprogram, but at run-time the overriding
body is invoked (see 3.9.2(20) second to last sentence).

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

From: Christoph Grein
Sent: Wednesday, May 15, 2002  11:33 PM

From: Tucker Taft <stt@avercom.net>

> It is true that it uses the formal parameter names and defaults from the
> ancestor primitive (since that is all that it knows at generic-compile-time),
> but it still invokes the body of the actual's primitive at run-time (see the
> last sentence of 12.5.1(21/1)).  So it would be bad news if this were
> abstract.

This was also my interpretation. It seems obvious to non-language lawyers.
[In Robert's wording: I do not see how you could think otherwise. :-]

> This is analagous to what happens when a primitive is overridden in the
> private part of a package.  The callers use the formal parameter names
> and defaults of the inherited subprogram, but at run-time the overriding
> body is invoked (see 3.9.2(20) second to last sentence).

package P0 is

  type T0 is tagged null record;
  procedure Op (X: T0);

end P0;

with P0;

package P1 is

  type T1 is new P0.T0 with null record;

private

  procedure Op (Y: T1);  -- overrides invisibly with new parameter name

end P0;

Here P1.Op, when called with named association, has to use Y where the private
part is visible, X where it is invisible, but both times the same overriding
procedure is called.

[Gnat 3.13p had problems with this.]

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

From: Randy Brukardt
Sent: Thursday, May 16, 2002  10:19 PM

> Well, I am not going to take "Tuck said so" for an answer, and I am going
> to ask the Rapporteur and the Editor to create an AI for this.

Will you take Randy said so instead? :-)

Here is the AI you requested (now AI-294). I have written it up as a
confirmation, following Robert's rule as Christophe reminded us. In this case,
because other interpretations of the paragraph lead to nonsense, and I think
that trying to define "corresponding" here formally probably will introduce a
lot more problems that it will fix.

Anyway, here's the AI:

(* Omitted. This was version /01. *)

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

From: Christoph Grein
Sent: Friday, May 17, 2002  2:13 AM

Just a wording improvement proposal.

> summary
>
> It is illegal to instantiate a generic with an abstract formal derived type
                                         ~~~~ which has
> parameter that inherits a concrete operation with a type that provides an
> abstract version of the concrete operation.

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

From: Steven W. Baird
Sent: Wednesday, May  7, 2003  3:55 PM

3.9.3(9), sentence 2, says:
   If a generic formal type is abstract, then
   for each primitive subprogram of the formal that
   is not abstract, the corresponding primitive subprogram
   of the actual shall not be abstract.

AI-294 makes the point that some kind of wording change is needed
to make it clear that the "corresponding" subprogram in question
is not the same as the "corresponding" subprogram of 3.4(17).

I believe that it would suffice to add the word "nonoverridden"
(possibly in parens) immediately after the word "corresponding".

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


Questions? Ask the ACAA Technical Agent