Version 1.5 of ai05s/ai05-0131-1.txt

Unformatted version of ai05s/ai05-0131-1.txt version 1.5
Other versions for file ai05s/ai05-0131-1.txt

!standard 12.6(10)          11-03-11 AI05-0131-1/03
!class binding interpretation 08-12-04
!status Amendment 2012 11-03-11
!status ARG Approved 7-0-1 11-02-20
!status work item 08-12-04
!status received 08-10-23
!priority Low
!difficulty Medium
!qualifier Omission
!subject Class-wide operations for formal subprograms revisited
!summary
The equivalence defined in AI05-0071-1 for formal subprogram matching is extended such that it applies to explicit as well as default actual subprogram. The equivalence is also explicitly given use-visibility, so that explicitly named, directly visible subprograms have preference over the implicitly created equivalent subprograms.
!question
AI05-0071-1 allows an instantiation to automatically create a matching subprogram for a class-wide type if a primitive for the specific type is directly visible.
However, this requires using a use clause to get the proper visibility, which can clutter the visibility with many unrelated subprograms. Moreover, the capability is not available if the subprogram is directly specified in the instantiation.
It is very unusual in Ada to be able to do something by default that you cannot do explicitly.
Should this be corrected? (Yes.)
!recommendation
(See Summary.)
!wording
Add after 8.4(8.2):
Certain implicit declarations may become potentially use-visible in certain contexts as described in 12.6.
Add after 12.6(9):
If a subtype_mark in the profile of the formal_subprogram_declaration denotes a formal private or formal derived type and the actual type for this formal type is a class-wide type T'Class, then for the purposes of resolving the corresponding actual subprogram at the point of the instantiation, certain implicit declarations may be available as possible resolutions as follows:
For each primitive subprogram of T that is directly visible at the point of the instantiation, and that has at least one controlling formal parameter, a corresponding implicitly declared subprogram with the same defining name, and having the same profile as the primitive subprogram except that T is systematically replaced by T'Class in the types of its profile, is potentially use-visible. The body of such a subprogram is as defined in 12.5.1 for primitive subprograms of a formal type when the actual type is class-wide.
AARM Implementation Note:
Although the above wording seems to require constructing implicit versions of all of the primitive subprograms of type T, it should be clear that a compiler only need to consider those that could possibly resolve to the corresponding actual subprogram. For instance, if the formal subprogram is a procedure with two parameters, and the actual subprogram name is Bar (either given explicitly or by default), the compiler need not consider primitives that are functions, that have the wrong number of parameters, that have defining names other than Bar, and so on; thus it does not need to construct implicit declarations for those primitives.
Undo the 12.6(10) wording changes of AI05-0071, leaving only the original (i.e., pre-AI05-0071) first sentence:
If a generic unit has a subprogram_default specified by a box, and the corresponding actual parameter is omitted, then it is equivalent to an explicit actual parameter that is a usage name identical to the defining name of the formal.
!discussion
Consider an example like:
package Pack1 is type Root is tagged record F1 : Integer; end record; procedure Oper_1 (X : in out Root); end Pack1;
package Pack2 is generic type T(<>) is private; with procedure Oper_1 (X : in out T) is <>; package Gen_Pack is end Gen_Pack; end Pack2;
with Pack1; with Pack2; package Pack3 is package Inst1 is new Pack2.Gen_Pack (Pack1.Root); -- ERROR: package Inst2 is new Pack2.Gen_Pack (Pack1.Root'Class); -- ERROR: package Inst3 is new Pack2.Gen_Pack (Pack1.Root, Pack1.Oper_1); -- OK package Inst4 is new Pack2.Gen_Pack (Pack1.Root'Class, Pack1.Oper_1); -- !! use Pack1; package Inst5 is new Pack2.Gen_Pack (Pack1.Root); -- OK package Inst6 is new Pack2.Gen_Pack (Pack1.Root'Class); -- OK end Pack3;
Inst1 is illegal because Oper_1 is not directly visible at the point of the instantiation. Inst2 is illegal for a similar reason. The use clause makes the similar Inst5 and Inst6 legal, the latter because of the new rules of AI05-0071-1.
But if you want to explicitly give the subprogram (perhaps because the use clause drags in lots of other conflicting names), Inst3 is legal (of course), but Inst4 is not (without the changes of this AI).
Ada almost always allows using selected notation rather than use visibility (it even goes so far as to provide selected notation for operators!), but that is not the case here. That seems to be a mistake.
----
AI05-0071-1 also introduced another problem (noticed while rewording to fix the problem in the question). Since it doesn't explain where the implicit subprogram is declared, it is not clear how it interacts with explicit routines.
For instance, consider the following case where the instance would have been legal without AI05-0071-1:
package Pkg1 is type T1 is tagged null record; procedure Foo (X1 : T1);
generic type T2 (<>) is private; with procedure Foo (X2 : T2) is <>; package G is end G; end Pkg1;
with Pkg1; package Pkg2 is use Pkg1; procedure Foo (X : T1'Class); package I is new G (T1'Class); -- Ambiguous, and thus illegal? end Pkg2;
It is hard to figure out how the visibility rules apply to the above example because the AI05-0071 wording just says that a "Foo" with the right profile "is directly visible" without defining the declaration point (and thus, the scope) of this "Foo".
In any case, it seems unfriendly to reject the above instantiation. It seems clear what the user wants.
One could imagine an approach using (in effect) a preference rule, where the implicit declaration is only referenced if it is needed. This would introduce Beaujolais effects because adding/deleting a use clause could affect the need for the implicit declaration.
Consider the variation of the above example where we replace
procedure Foo (X : T1'Class);
with
package Foo_Pkg is procedure Foo (X : T1'Class); end Foo_Pkg; use Foo_Pkg;
and then consider the effect of deleting the "use Foo_Pkg;". This illustrates how a preference rule could introduce Beaujolais effects.
To deal with this aspect of the problem, the proposed wording says that the implicit declaration is not "directly visible", but rather "potentially use-visible".
This is the only part of the wording changes that is motivated by this "homograph collision" problem (along with the related 8.4 changes). The rest of the wording changes are motivated by the problem that this AI was originally created to address.
It seems odd to introduce the possibility of a "potentially use-visible" declaration in a program which does not contain the reserved word "use" anywhere, but this seems to solve the problem and no better approach has been found.
----
The original example which motivated AI05-0071-1 remains legal, but now this is handled via the equivalence stated in the one sentence of 12.6(10) that has remained constant throughout all of these changes (listed above), as opposed to via an only-for-defaulted-parameters rule.
Note that this proposal follows AI05-0071-1 in building implicit declarations only for primitive subprograms which have at least one controlling formal parameter (as opposed to only a controlling result), AI05-0071-1 got this part right - building on 12.5.1(23.3/2)'s weirdness seems like a bad idea. With at least one controlling operand, we have a nice clean wrapper model. When we only have a controlling result, things get messy.
Note that with the proposed changes, the
package Foo_Pkg is procedure Foo (X : T1'Class); end Foo_Pkg; use Foo_Pkg;
version of the example described above will be rejected (which is a good thing - we don't want Beaujolais effects).
It would also be rejected (for the same reason) if the actual parameter were specified explicitly via a simple name, as in
package Foo_Pkg is procedure Foo (X : T1'Class); end Foo_Pkg; use Foo_Pkg; package I is new G (T1'Class, Foo);
but would be accepted if the name of the actual were qualified, as in
package Foo_Pkg is procedure Foo (X : T1'Class); end Foo_Pkg; use Foo_Pkg; package I is new G (T1'Class, Foo_Pkg.Foo);
(and note that adding/deleting the "use Foo_Pkg;" in this version of the example would make no difference - that use clause has no effect on this example even if it is present). This shows that there is a straighforward workaround to any ambiguities created.
--!corrigendum 12.6(10/2)
!ACATS Test
An ACATS C-test should test the revised rule.
!appendix

!topic Should AI05-0071 allow renaming subprograms?
!reference AI05-0071
!from Adam Beneschan 08-10-23
!discussion

The language of AI05-0071 allows for a generic to be instantiated with a
class-wide type, and will allow the actuals for generic formal subprograms
to be dispatching subprograms "created" by the compiler, in a sense.  However,
the language of AI05-0071 allows for this only if the operation is a primitive
subprogram that is directly visible.

I'm wondering whether this should be expanded a bit to allow renames.
Consider this example, which doesn't involve instantiation with class-wide
types:

    package Pack1 is
        type Root is tagged record
            F1 : Integer;
        end record;
        procedure Oper_1 (X : in out Root);
    end Pack1;

    package Pack2 is
        generic
            type T(<>) is private;
            with procedure Oper_1 (X : in out T) is <>;
        package Gen_Pack is
        end Gen_Pack;
    end Pack2;

    with Pack1;
    with Pack2;
    package Pack3 is
        package Inst is new Pack2.Gen_Pack (Pack1.Root);  -- ERROR:
    end Pack3;

This won't compile, because for the generic formal subprogram default to be used,
Oper_1 would have to be visible.  This can be accomplished with USE or USE TYPE.
But another way, if it is undesirable to make everything directly visible that
USE or USE TYPE would make visible, would be to use a rename:

    package Pack3 is
        procedure Oper_1 (X : in out Pack1.Root) renames Pack1.Oper_1;
        package Inst is new Pack2.Gen_Pack (Pack1.Root);
    end Pack3;

This option doesn't appear to be available in the class-wide type case:

    package Pack3 is
        procedure Oper_1 (X : in out Pack1.Root) renames Pack1.Oper_1;
        package Inst is new Pack2.Gen_Pack (Pack1.Root'Class);
    end Pack3;

The new language that AI05-0071 adds to 12.6(10) wouldn't apply because this
subprogram rename isn't a primitive subprogram, and the subprogram that it
renames, which is a primitive subprogram, is still not directly visible.

It may not be the worst problem in the world, and you can work around it, but it's
a little ugly:

    package Pack3 is
        package Dummy_Nested is
            use type Pack1.Root;
            package Inst is new Pack2.Gen_Pack (Pack1.Root'Class);
        end Dummy_Nested;
        package Inst renames Dummy_Nested.Inst;
    end Pack3;

But should 12.6(10) as modified by this AI be modified to allow directly visible
subprograms that are renames of primitive subprograms, as well as directly visible
primitive subprograms?

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

From: Adam Beneschan
Date: Thursday, October 23, 2008 12:34 PM

> This won't compile, because for the generic formal subprogram default
> to be used, Oper_1 would have to be visible.  This can be accomplished
> with USE or USE TYPE.

Ummm, my bad.  USE TYPE wouldn't work in the above case, but it would work
if the subprogram were an operator.  This actually makes the problem potentially
a little worse than I thought, since it means that in the AI95-71 case, if the
subprogram isn't an operator, you'd have to use USE instead of USE TYPE to make
it directly visible, and there could be even more objection to that since it
could make many other things directly visible.

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

From: Tucker Taft
Date: Thursday, October 23, 2008 12:42 PM

I don't see the need.  This is a tricky enough implementation area as it is,
so trying to wedge renames into this seems like overkill.  By the way, a
"use type" wouldn't help here because we aren't talking about an operator.

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

From: Randy Brukardt
Date: Friday, October 24, 2008  7:39 PM

Those of us from the society of use-adverse programmers would beg to differ.

But I don't want renames, either, the problem in my mind is that the magic
doesn't work for an explicit routine (only one that is defaulted). This is
about the only case in Ada that I can think of where that is true.

In Adam's example, I almost certainly would have given the parameter explicitly
rather than insert a use clause:

  with Pack1;
  with Pack2;
  package Pack3 is
      package Inst is new Pack2.Gen_Pack (Pack1.Root, Pack1.Oper_1);
  end Pack3;

which is a lot less work (and maintainance issues) than a renames!

But the equivalent:

  with Pack1;
  with Pack2;
  package Pack4 is
      package Inst is new Pack2.Gen_Pack (Pack1.Root'Class, Pack1.Oper_1);
             -- ERROR:
  end Pack4;

is illegal, because Oper_1 doesn't match. The only choices are to put in a
full use clause (no way) or to define your own Oper_1 (note that I don't see
any value to a rename here, either, the full routine isn't much longer):

  with Pack1;
  with Pack2;
  package Pack4 is
      procedure Oper_1 (X : in out Pack1.Root'Class);
      package Inst is new Pack2.Gen_Pack (Pack1.Root'Class, Oper_1);
  end Pack4;

  package body Pack4 is
      procedure Oper_1 (X : in out Pack1.Root'Class) is
      begin
          Pack1.Oper_1 (X);
      end Oper_1;
  end Pack4;

Obviously, this extra routine adds additional junk to maintain and an additional
source of errors. And it's weird that an implicit subprogram can be built if
no name is given here, but giving an explicit name loses this option.

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

From: Tucker Taft
Date: Friday, October 24, 2008  9:00 PM

I would be more sympathetic to making the explicit parameter work than making
the rename work.  I would think the adjustment to the wording to make the
explicit parameter work might not be too horrendous.

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

From: Steve Baird
Date: Friday, February  4, 2011  1:34 PM

> Summary of action items:
> Steve:
>       AI05-0131-1  Work with Randy and Gary to create a complete
>        proposal for this AI.

Here is a proposal for this AI.
Thanks to Randy and Gary for their review of an earlier version and suggested
improvements.

As always, feedback would be appreciated.

   -- Steve

--------------

AI05-0071 introduced two distinct problems.

There is the problem that originally motivated the current AI, the inconsistency
between the treatment of defaulted and non-defaulted actual subrogram parameters
for an instance.

There is also the problem of determining what AI05-0071 means (and fixing it if
we don't like the answer) in the case where the instance would have been legal
without AI05-0071, as in

     package Pkg1 is
       type T1 is tagged null record;
       procedure Foo (X1 ; T1);

       generic
         type T2 (<>) is private;
         with procedure Foo (X2 : T2) is <>;
       package G is
       end G;
     end Pkg;

     with Pkg1;
     package Pkg2 is
       use Pkg1;
       procedure Foo (X : T1'Class);
       package I is new G (T1'Class); -- ambiguous, and thus illegal?
     end Pkg2;

Recall that AI05-0071 introduces the following mouthful:
   If a subtype_mark in the profile of the formal_subprogram_declaration
   denotes a formal private or formal derived type and the actual type
   for this formal type is a class-wide type T'Class, then for the
   purposes of resolving this default_name at the point of the
   instantiation, for each primitive subprogram of T that has a matching
   defining name, that is directly visible at the point of the
   instantiation, and that has at least one controlling formal parameter,
   a corresponding subprogram with the same defining name is directly
   visible, but with T systematically replaced by T'Class in the types of
   its profile. The body of such a subprogram is as defined in 12.5.1 for
   primitive subprograms of a formal type when the actual type is
   class-wide.

It is hard to figure out how the visibility rules apply to the above example
because the AI05-0071 wording just says that a "Foo" with the right profile "is
directly visible" without defining the declaration point (and thus, the scope)
of this "Foo".

In any case, it seems unfriendly to reject the above instantiation.
It seems clear what the user wants.

One could imagine an approach using (in effect) a preference rule, where the
implicit declaration is only referenced if it is needed. This would introduce
Beaujolais effects because adding/deleting a use clause could affect the need
for the implicit declaration.

Consider the variation of the above example where we replace
     procedure Foo (X : T1'Class);
with
     package Foo_Pkg is
       procedure Foo (X : T1'Class);
     end Foo_Pkg;
     use Foo_Pkg;

and then consider the effect of deleting the "use Foo_Pkg;".
This illustrates how a preference rule could introduce Beaujolais effects.

To deal with this aspect of the problem, the proposed wording given below says
that the implicitly declared declaration is not "directly visible", but rather
"potentially use-visible".

This is the only part of the wording changes that is motivated by this
"homograph collision" problem. The rest of the wording changes are motivated by
the problem that this AI was originally created to address.

It seems odd to introduce the possibility of a "potentially use-visible"
declaration in a program which does not contain the reserved word "use"
anywhere, but this seems to solve the problem and no better approach has been
found.

The proposed wording for this AI:

====

!wording

Add after 8.4(8.2)
   Certain implicitly declared declarations may become potentially
   use-visible in certain contexts as described in 12.6.

Add after 12.6(9):
   If a subtype_mark in the profile of the formal_subprogram_declaration
   denotes a formal private or formal derived type and the actual type
   for this formal type is a class-wide type T'Class, then for the
   purposes of resolving the corresponding actual subprogram at the
   point of the instantiation, certain implicitly declared declarations
   may be available as possible resolutions as follows:
     For each primitive subprogram of T that has a matching defining
     name, that is directly visible at the point of the instantiation,
     and that has at least one controlling formal parameter, a
     corresponding implicitly declared subprogram with the same
     defining name, and having the same profile as the primitive
     subprogram except that T is systematically replaced by T'Class
     in the types of its profile, is potentially use-visible.
     The body of such a subprogram is as defined in 12.5.1 for primitive
     subprograms of a formal type when the actual type is class-wide.

Undo the 12.6(10) wording changes of AI05-0071, leaving only the original (i.e.,
pre-AI05-0071) first sentence:

   If a generic unit has a subprogram_default specified by a box, and the
   corresponding actual parameter is omitted, then it is equivalent to an
   explicit actual parameter that is a usage name identical to the
   defining name of the formal.

====

The original example which motivated AI05-0071 remains legal, but now this is
handled via the equivalence stated in the one sentence of 12.6(10) that has
remained constant throughout all of these changes (listed above), as opposed to
via an only-for-defaulted-parameters rule.

Note that this proposal follows AI05-0071 in building implicit declarations only
for primitive subprograms which have at least one controlling formal parameter
(as opposed to only a controlling result), AI05-0071 got this part right -
building on 12.5.1(23.3/2)'s weirdness seems like a bad idea. With at least one
controlling operand, we have a nice clean wrapper model. When we only have a
controlling result, things get messy.

Note that with the proposed changes, the
     package Foo_Pkg is
       procedure Foo (X : T1'Class);
     end Foo_Pkg;
     use Foo_Pkg;
version of the example described above will be rejected (which is a good thing -
we don't want Beaujolais effects).

It would also be rejected (for the same reason) if the actual parameter were
specified explicitly via a simple name, as in

     package Foo_Pkg is
       procedure Foo (X : T1'Class);
     end Foo_Pkg;
     use Foo_Pkg;
     package I is new G (T1'Class, Foo);

but would be accepted if the name of the actual were qualified, as in

     package Foo_Pkg is
       procedure Foo (X : T1'Class);
     end Foo_Pkg;
     use Foo_Pkg;
     package I is new G (T1'Class, Foo_Pkg.Foo);

(and note that adding/deleting the "use Foo_Pkg;" in this version of the example
would make no difference - that use clause has no effect on this example even if
it is present).

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

From: Bob Duff
Date: Friday, February  4, 2011  2:17 PM

> As always, feedback would be appreciated.

Looks good to me.

> given below says that the implicitly declared declaration is

"implicitly declared declaration" --> "implicit declaration"
throughout.  Declarations aren't declared, unless you're the DOR Dept.  ;-)

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

From: Steve Baird
Date: Friday, February  4, 2011  2:33 PM

> "implicitly declared declaration" --> "implicit declaration"
> throughout.  Declarations aren't declared, unless you're the DOR Dept.
> ;-)
>

Sounds right.

You probably don't get your cash from an ATM machine either.

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

From: Randy Brukardt
Date: Friday, February  4, 2011  11:39 PM

...
> You probably don't get your cash from an ATM machine either.

Gee, I do. :-) I also use the ACATS test suite all of the time. (Indeed, I
usually refer to it that way on comp.lang.ada so the reference is clear to
newbies, even though that's technically redundant.)

BTW, it took me a long time (until just now, even though I first read this
message when it was sent 10 hours ago) to even figure out what you were getting
at.

In any case, I've turned your original message into a proper AI (with the
change), so this item can be crossed off from the open action item list.

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

From: Randy Brukardt
Date: Saturday, February  5, 2011  12:04 AM

Wording issue:

...
> Add after 12.6(9):
>    If a subtype_mark in the profile of the formal_subprogram_declaration
>    denotes a formal private or formal derived type and the actual type
>    for this formal type is a class-wide type T'Class, then for the
>    purposes of resolving the corresponding actual subprogram at the
>    point of the instantiation, certain implicit declarations
>    may be available as possible resolutions as follows:
>      For each primitive subprogram of T that has a matching defining
>      name, that is directly visible at the point of the instantiation,
>      and that has at least one controlling formal parameter, a
>      corresponding implicitly declared subprogram with the same
>      defining name, and having the same profile as the primitive
>      subprogram except that T is systematically replaced by T'Class
>      in the types of its profile, is potentially use-visible.
>      The body of such a subprogram is as defined in 12.5.1 for primitive
>      subprograms of a formal type when the actual type is class-wide.

The indented wording starts "For each primitive subprogram of T that has a
matching defining name, ..."

"matching defining name" to what?? The wording never says, at least not clearly.

In AI05-0071-1, it was matching the name of the formal subprogram, but that
isn't right here -- if the name is given explicitly, we want routines that match
that name, not anything else.

I'm not sure it is worth trying to restrict the routines that get created here.
Use visibility seems to eliminate most problems that could occur, and the
limitation to use for "resolving the corresponding actual subprogram" seems to
eliminate the rest. So I would just drop "matching defining name". If we do want
to restrict them, we need a lot more complex wording than "matching defining
name"!

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

From: Randy Brukardt
Date: Saturday, February  5, 2011  12:28 AM

Having thought about this some more, it seems wrong to mention the defining name in
any case. There are lots of properties which would prevent a candidate subprogram from
matching a formal subprogram: wrong "kind" (procedure vs. function), wrong name,
wrong number of parameters, wrong type for some parameter or the result, etc. Any
property of a type conformant profile can be used in resolution, along with the
defining name (that's what "expected profile" means). So why hone in on that one
property: they're all equally relevant.

So I dropped the "matching defining name" wording, and added the following AARM note:

AARM Implementation Note:
   Although the above wording seems to require constructing implicit
   versions of all of the primitive subprograms of type T, it should be
   clear that a compiler only need to consider those that could possibly
   resolve to the corresponding actual subprogram. For instance, if the formal
   subprogram is a procedure with two parameters, and the actual subprogram
   name is Bar (either given explicitly or by default), the compiler
   need not consider primitives that are functions, that have the wrong
   number of parameters, that have defining names other than Bar, and so on;
   thus it does not need to construct implicit declarations for those
   primitives.

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

From: Steve Baird
Date: Monday, February  7, 2011  11:29 AM

> Having thought about this some more, it seems wrong to mention the 
> defining name in any case.

I agree - the changes you've made look good to me.

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

From: Tucker Taft
Date: Monday, February  7, 2011  11:46 AM

Why does this restrict itself to primitive subprograms of T that "have at
least one controlling formal parameter"? This may have been explained
elsewhere, but I must have missed it.  Why aren't all primitives of T
made use-visible with the given "T => T'Class" substitution, even if they
only have controlling results?

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

From: Steve Baird
Date: Monday, February  7, 2011  12:05 PM

From my previous e-mail:

> Note that this proposal follows AI05-0071 in building implicit 
> declarations only for primitive subprograms which have at least one 
> controlling formal parameter (as opposed to only a controlling 
> result), AI05-0071 got this part right - building on 12.5.1(23.3/2)'s 
> weirdness seems like a bad idea. With at least one controlling 
> operand, we have a nice clean wrapper model. When we only have a 
> controlling result, things get messy.

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

From: Randy Brukardt
Date: Monday, February  7, 2011  12:09 PM

Steve explained it in the !discussion of the AI.

Since such routines are defined to raise Program_Error in many circumstances,
it seems unhelpful to allow it, and a lot of extra work as well.

In any case, this was carried over from the wording of AI05-0071-1, which
we didn't intend to change.

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

From: Tucker Taft
Date: Monday, February  7, 2011  12:18 PM

Thanks.  I had missed the significance of this originally.

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

Questions? Ask the ACAA Technical Agent