!standard 8.4(8/2) 11-05-05 AI05-0131-1/04 !standard 12.6(9) !standard 12.6(10) !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): 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 needs 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 8.4(8/2) For each package named in a @fa whose scope encloses a place, each declaration that occurs immediately within the declarative region of the package is @i at this place if the declaration is visible at this place. For each type @i or @i'Class named in a @fa whose scope encloses a place, the declaration of each primitive operator of type @i is potentially use-visible at this place if its declaration is visible at this place. @dinsa Certain implicit declarations may become potentially use-visible in certain contexts as described in 12.6. !corrigendum 12.6(9) @dinsa A @fa declares a generic formal subprogram. The types of the formal parameters and result, if any, of the formal subprogram are those determined by the @fas given in the @fa; however, independent of the particular subtypes that are denoted by the @fas, the nominal subtypes of the formal parameters and result, if any, are defined to be nonstatic, and unconstrained if of an array type [(no applicable index constraint is provided in a call on a formal subprogram)]. In an instance, a @fa declares a view of the actual. The profile of this view takes its subtypes and calling convention from the original profile of the actual entity, while taking the formal parameter @fas and @fas from the profile given in the @fa. The view is a function or procedure, never an entry. @dinst If a @fa in the profile of the @fa denotes a formal private or formal derived type and the actual type for this formal type is a class-wide type @i'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: @xindent 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 @i is systematically replaced by @i'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.> !comment The changes to 12.6(10) were removed from AI05-0071-1, so !comment we don't need to mention that clause here. !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. ****************************************************************