Version 1.2 of ai22s/ai22-0024-1.txt

Unformatted version of ai22s/ai22-0024-1.txt version 1.2
Other versions for file ai22s/ai22-0024-1.txt

!standard 3.9.3(7/5)          22-01-20 AI22-0024-1/01
!standard 4.1.3(9.2/3)
!class binding interpretation 22-01-20
!status work item 22-01-20
!status received 21-04-28
!priority Low
!difficulty Easy
!qualifier Clarification
!subject Abstract prefixed views
!summary
** TBD **
!question
Following is a distillation of another ACATS test that gets different results in different Ada 2012 compilers:
procedure B950001 is
package Nested is type Intf is synchronized interface; procedure PEN1 (Param : in out Intf) is abstract with Synchronization => By_Entry; end Nested;
generic with procedure P; package Gen1 is Decl : Natural; end Gen1;
procedure Test (Intf_In_Parm : in Nested.Intf'Class; Intf_In_Out_Parm : in out Nested.Intf'Class) is package GPE139 is new Gen1 (Intf_In_Parm.PEN1); -- ERROR: Prefix is constant package GPE149 is new Gen1 (Intf_In_Out_Parm.PEN1); -- OK. Prefix is variable begin null; end Test;
...
The test is trying to check legality of prefixes of prefixed views.
However, one of the tested compilers rejected the "OK" instantiation because the subprogram is abstract.
The actual here is a prefixed view of an abstract subprogram, but this view is of a dispatching call that is legal for an actual call. But is it legal as the actual of a generic subprogram? Should it be?
The test also renames these calls:
procedure RPE139 renames Intf_In_Parm.PEN1; -- ERROR: Prefix is constant procedure RPE149 renames Intf_In_Out_Parm.PEN1; -- OK. Prefix is variable
Would a call on RPE149 be legal? If the prefixed view is abstract, it would be odd if a call on the renames is legal (the dispatching is bound in the prefix).
!recommendation
** TBD. **
!wording
** TBD **
!discussion
4.1.3(9.2/3) defines a prefixed view to be a view of a subprogram; is not itself a call; it is a subprogram that is called in the usual way (with one less parameter).
6.4(9.1/5) makes it clear that a call on a prefixed view is equivalent to the underlying usual call. So a dispatching call on a prefixed view should be allowed.
However, there don't appear to be any description of the properties of the prefixed view itself. This matters when the prefixed view is renamed or used as the actual for a generic formal subprogram. In particular, is the renamed prefixed view still abstract even if the prefixed view represents part of a dispatching call? We need to answer this.
----
We need to break down this problem into a number of cases:
In the case where there the first parameter of the underlying abstract subprogram is the only controlling parameter, then there are two cases:
* if the type of the prefix is class-wide, a call would be a legal dispatching call and the other parameters (if any) have no effect on that. So in this case, we should allow the prefixed view to be created, and it should not be abstract;
* otherwise (the type of the prefix is not class-wide), a call would be an illegal call on an abstract operation, and again the other parameters have no effect on that. So in that case, the prefixed view should be illegal.
[Note: In this latter case, we want to make the prefixed view illegal to follow the language design principle that if a declaration creates an item for which any use (in this case, call) would be illegal, then the declaration itself should be illegal.]
In the case where the first parameter of the underlying abstract subprogram is not a controlling parameter, then one or more of the remaining parameters has to be the controlling parameter(s). In such a case, the legality of the call depends on actual values for those other parameters -- the prefixed view should still act as if it is abstract. Ergo, we allow the prefixed view, and it still is abstract.
Finally, in other cases, we have more than one controlling parameter with one of them being the first. Creating a prefixed view of such a subprogram would cause a rather weird "partially dispatching" subprogram, where both explicit and implicit parameters are controlling the dispatching. Depending on how that was interpreted, one could have a mixed statically and dynamically bound call, a tag check not obvious in the code, or an illegal call. In any case, one would need additional "strings" (either statically or dynamically) in order to match the semantics of either the fully expanded call or the profile of the prefixed view (and it would not be possible to match the semantics of both exactly, as it is with other calls).
It might help to look at an example:
package P1 is type Intf is interface; function F1 (A : in Intf; B : in Natural) return Boolean is abstract; function F2 (A : in Natural; B : in Intf) return Boolean is abstract; function F3 (A, B : in Intf) return Boolean is abstract; end P1;
procedure Test (Obj : in P1.Intf'Class; Val : in Natural) is function R1 (B : in Natural) return Boolean renames Obj.F1; -- (1) function R2 (B : in P1.Intf) return Boolean renames Val.F2; -- (2) function R3 (B : in P1.Intf) return Boolean renames Obj.F3; -- (3) begin if R1(1) then -- Should be OK. P1.F1(Obj, 1) certainly is legal. null; elsif R2(Obj) then -- Should be OK. P1.F2(Val, Obj) certainly is legal. null; elsif R3(A) then -- Could be OK, but very strange. null; end if; end Test;
R1 and R2 demonstrate the first two cases above. R1 is treated as not being abstract - the dispatching is "hidden" in the prefixed view.
On the other hand, R2 is still an abstract routine that needs a class-wide actual parameter in order to be a dispatching call. (??? - note that it isn't primitive. What happens for a rename of an abstract routine with a controlling parameter in a place that is not primitive?)
Finally, R3 demonstrates the last case. Note that the profile of R3 has only a single controlling parameter; moreover, by language rules it isn't even primitive. Regardless of what semantics are chosen for R3, it cannot have the same semantics as both the given profile and the the fully expanded call.
Note that the oddities don't require the underlying subprogram to be abstract. We think the best solution is the make such bizarre prefixed views illegal.
!ACATS test
The existing B950001 contains these cases (now commented out).
!appendix

From: Randy Brukardt
WG 9 Review issue #175 - May 22, 2021

Following is a distillation of another ACATS test that gets different results 
in different Ada 2012 compilers:

procedure B950001 is

   package Nested is
      type Intf is synchronized interface;
      procedure PEN1 (Param : in out Intf) is abstract
         with Synchronization => By_Entry;
   end Nested;

   generic
      with procedure P;
   package Gen1 is
      Decl : Natural;
   end Gen1;

   procedure Test (Intf_In_Parm : in Nested.Intf'Class;
                   Intf_In_Out_Parm : in out Nested.Intf'Class) is
      package GPE139 is new Gen1 (Intf_In_Parm.PEN1); -- ERROR: Prefix is constant
      package GPE149 is new Gen1 (Intf_In_Out_Parm.PEN1); -- OK. Prefix is variable
begin
   null;
end Test;

...

The test is trying to check legality of prefixes of prefixed views.

However, one of the tested compilers rejected the "OK" instantiation because
the subprogram is abstract.

The actual here is a prefixed view of an abstract subprogram, but this view is
of a dispatching call that is legal for an actual call. But is it legal as the
actual of a generic subprogram? Should it be?

I can't find anything definitive. 3.9.3(7/5) suggests that the subprogram that 
is called is abstract, but whether that is still the case for a generic actual
is unclear.

It seems weird at best that you can call the subprogram but that you couldn't 
pass it as a generic actual. Could there be a reason for that??

I'd expect a similar issue with an instance without the synchronization, but I
have not tested (or analyzed) that case.

The test also renames these calls:

  procedure RPE139 renames Intf_In_Parm.PEN1;     -- ERROR: Prefix is constant
  procedure RPE149 renames Intf_In_Out_Parm.PEN1; -- OK. Prefix is variable

Would a call on RPE149 be legal? If the prefixed view is abstract, it would be
odd if a call on the renames is legal (the dispatching is bound in the prefix).

Tucker Taft suggested that a potential precedent might be the rules for what 
happens when you instantiate a generic having a formal extension parameter
with a class-wide type. Effectively wrappers are created for each of the
primitives, which take 'Class parameters and do the dispatching-related checks
and the actual dispatching call internally. We should think of a prefixed-view
of a subprogram that has a class-wide prefix as effectively a non-abstract
wrapper, in analogy with the rules given in 12.5.1(23.1-23.3). Thus the lines
marked as "OK" are in fact "OK."

Note that the operation should still be abstract if the call isn't dispatching 
(that is, the prefix is not the controlling parameter of the call).

This one needs quite a bit of wording and thought (it's not at all simple), so
it is best deferred.

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

Questions? Ask the ACAA Technical Agent