!standard 8.3(26) 03-04-28 AI95-00218-02/04 !class amendment 02-09-30 !status No Action (10-0-0) 04-03-05 !status work item 02-09-30 !status received 02-09-30 !priority Medium !difficulty Medium !subject Accidental overloading when overriding !summary In order to reduce problems with overriding of primitive operations, additional syntax for subprogram declarations is introduced. A pragma controls the use of this syntax. !problem Subtle bugs result from mistakes in the profile of an overriding subprogram. For example: with Ada.Finalization; package Root is type Root_Type is new Ada.Finalization.Controlled with null record; procedure Do_Something (Object : in out Root_Type; Data : in Natural); -- (1) procedure Finalize (Object : in out Root_Type);-- (2) end Root; with Root; package Leaf is type Derived_Type is new Root.Root_Type with null record; procedure Do_Something (Object : in out Derived_Type; Data : in Boolean); -- (3) procedure Finalise (Object : in out Derived_Type); -- (4) -- Note: Alternative spelling of "Finalize". end Leaf; with Leaf; procedure Sample is Obj : Leaf.Derived_Type; begin Leaf.Do_Something (Obj, 10); -- Calls (1). -- Finalization of Obj will call (2). end Sample; Assume the programmer intended (3) and (4) to override (1) and (2), but made subtle declaration errors. Because (1) and (3) are not homographs, the declaration of (3) is legal. However, (3) does not override (1) as intended. Therefore, the call in Sample calls the wrong routine. Similarly, the incorrect declaration of "Finalise" (4) does not override (2). Thus, the finalization of Sample.Obj will call the wrong routine. The resulting bugs are very difficult to find, especially because the programmer will probably look at this call several times before even thinking that the wrong routine might be called. The bug is even harder to find when the call is dispatching or implicit (as it is for Finalize), because the reason that the wrong routine is being called is not obvious. In the Finalize case, the programmer might even think that the compiler is failing to call Finalize at all. These problems have generated many "bug" reports to compiler writers, including ones from their own development teams. A more serious problem is that it makes maintenance of object-oriented Ada 95 code much more difficult. Consider a maintenance change to the root class which adds a parameter to Do_Something. All extensions of Root_Type that override Do_Something will now silently overload it instead. The change probably will break all existing code, yet provide no warnings of the problem. A rarer, but similar problem can occur when a routine accidentally overrides another. In this case, an inappropriate routine may be called via dispatching, and may cause the failure of an abstraction. All of these cases violate the Ada design principle of least surprise, as described in paragraph 8 of the Introduction to the standard. !proposal The problem of accidental overloading rather than overriding can be eliminated if the Ada compiler knows when the programmer wishes to override a routine. Similarly, accidental overriding can be minimized if the Ada compiler knows that the programmer has declared all intentional overriding. Unfortunately, a complete solution to this problem would be incompatible with existing Ada 95 code. Therefore, we have to introduce a solution which permits backward compatibility and applies fully only when the programmer asks for it. Thus, we introduce a new pragma, Explicit_Overriding, and additional syntax with a new nonreserved keyword which indicates that overriding will or might occur. Explicit_Overriding is a configuration pragma which requires use of the alternative syntax. The general idea is that subprogram declarations that are overriding are prefixed with "overriding" whereas subprogram declarations without this new syntax must not override another routine. The same applies to renamings and generic instantiations. Special situations arise with generic packages and with private types. In the case of generics with a formal type it is possible that whether a declaration in the generic specification is overriding or not depends upon the actual type. In order to avoid imposing additional contract requirements such declarations may be prefixed by "overriding or not" in which case no check is made. The form "overriding or not" may in fact be used in all circumstances and has the effect of turning off the checking for that declaration. In the case of a subprogram which is a visible operation of a private type (or private extension), checking may occur twice, once for the partial view and again for the full view. It might be that the operation is not visibly overloaded but turns out to be overloaded when the full type declaration occurs. In this case the operation in the partial view is not prefixed by "overriding" but an overriding clause has to be given in the private part which consists essentially of a copy of the specification prefixed by "overriding" (or even "overriding or not"). This overriding clause has to have full conformance with the specification in the partial view and since such conformance is not defined for generic instances, the partial view may not be an instance. Note that if a declaration is prefixed by "overriding" then an appropriate check is always made (even in the absence of the pragma Explicit_Overriding) since it would be confusing for such explicit syntax to be ignored. !wording In 2.9(3) as modified by AI 284 add the following to the list of nonreserved keywords overriding Change the syntax in 6.1 to read overriding_indicator ::= overriding [or not] subprogram_declaration ::= [overriding_indicator] subprogram_specification ; abstract_subprogram_declaration ::= [overriding_indicator] subprogram_specification is abstract; overriding_clause ::= overriding_indicator subprogram_specification ; Add to 8.3 after paragraph 23: If an explicit declaration of a visible operation of a private type or a private extension by a subprogram_declaration, abstract_subprogram_declaration, or subprogram_renaming_declaration does not include the overriding_indicator "overriding" then it is permitted to give an overriding_clause for the operation in the corresponding private part. Such an overriding_clause shall occur following the corresponding full_type_declaration and before the type is frozen. Its profile shall be fully conformant with the profile of the subprogram_declaration, abstract_subprogram_declaration or subprogram_renaming_declaration. Add to 8.3 after paragraph 26: If an explicit declaration of a visible operation of a private type or private extension by a subprogram_declaration, abstract_subprogram_declaration, subprogram_renaming_declaration or generic_instantiation of a subprogram has the overriding_indicator "overriding" then it shall be an overriding declaration for the partial view of the type. If an overriding_clause has the overriding indicator "overriding" then the corresponding operation shall be an overriding operation for the full view of the type. If an explicit declaration of an operation of a type which is neither a private type nor a private extension by a subprogram_declaration, abstract_subprogram_declaration, subprogram_renaming_declaration or generic_instantiation of a subprogram has the overriding_indicator "overriding" then it shall be an overriding declaration for the type. The form of a pragma Explicit_Overriding is as follows: pragma Explicit_Overriding; Pragma Explicit_Overriding is a configuration pragma. The configuration pragma Explicit_Overriding applies to all declarations within compilation units to which it applies, except that in an instance of a generic unit, Explicit_Overriding applies if and only if it applies to the generic unit. At a place where a pragma Explicit_Overriding applies, an explicit subprogram_declaration, abstract_subprogram_declaration, subprogram_renaming_declaration or generic_instantiation of a subprogram without an overriding_indicator shall not be an overriding declaration for the view of the type at that place. Moreover, unless such a declaration of an operation of a private type given in the visible part has a corresponding overriding clause in the private part then it shall not be an overriding declaration for the full view of the type. In addition to the places where Legality Rules normally apply, these rules also apply in the private part of an instance of a generic unit. AARM Note: The overriding required by these rules does not necessarily need to happen immediately at the declaration of a primitive operation. It could occur later because of a later implicit declaration in the declarative region (see 7.3.1). Change the syntax in 8.5.4 to read subprogram_renaming_declaration ::= [overriding_indicator] subprogram_specification renames callable_entity_name; Change the syntax in 12.3 to read generic_instantiation ::= package defining_program_unit_name is new generic_package_name [generic_actual_part]; | [overriding_indicator] procedure defining_program_unit_name is new generic_procedure_name [generic_actual_part]; | [overriding_indicator] function defining_designator is new generic_function_name [generic_actual_part]; !example Here is our original example using the new pragma and syntax. pragma Explicit_Overriding; with Root; package Leaf is type Derived_Type is new Root.Root_Type with null record; overriding procedure Do_Something (Object : in out Root_Type; Data : in Boolean); -- Error: overriding procedure Finalise (Object : in out Root_Type); -- Error: -- Note: Alternative spelling of "Finalize". end Leaf; The declarations of Do_Something and Finalise will now cause errors. The problem will immediately be pointed out to the programmer. !discussion The configuration pragma Explicit_Overriding is necessary for backward compatibility. If the pragma is applied then the prefix "overriding" is required on all subprogram declarations that do override. This checking prevents accidental overriding. Similarly if the pragma is applied then any subprogram declaration which does not override must not have the prefix. If the pragma is not applied and the prefix is omitted then there is no check that the subprogram does not override. However, if the prefix is given then even in the absence of the pragma, a check is still made that the subprogram does override. This provides maximum benefit with backward compatibility. The possibility of simply using the pragma to disable all overriding checks was considered but dismissed on the grounds that it would be very surprising for the programmer to be able to write "overriding" and then for the compiler to ignore it. The form "overriding or not" is required so that we can handle generic units where we cannot tell for sure whether overriding occurs. For instance, a mix-in generic does not care whether its operations override existing ones (the user of the generic may care, but that cannot be specified as part of the generic). Therefore, we provide "overriding or not" as a "don't care" marker, so that projects can still use the pragma Explicit_Overriding even if such generics are included. The effect of "overriding or not" can be thought of as restoring the Ada 95 rules for overriding for a particular declaration. Note that the forms "overriding" and "overriding or not" are part of a subprogram declaration and not the subprogram specification. This is because we do not want them to be permitted in all syntactic situations which use subprogram specification such as generic subprogram declarations, formal subprogram parameters and subprogram body stubs. However, we do want them to apply to abstract subprograms, to renaming and to generic instantiations. For example overriding procedure Op(X: T) is abstract; overriding procedure Pop(Y: TT) renames Pip; overriding procedure Pup is new Pap(P1 => A1, P2 => A2); Observe that we cannot use an overriding indicator with a subprogram body given alone but must supply a distinct subprogram declaration if we need to include one. Recall that in Ada 95, a subprogram body given alone in a package body can override an inherited primitive operation although it cannot be a new primitive operation. It would be surprising to permit the overriding indicator with such a body given alone since the existence of the overriding would not then be clear just from the package specification. This applies to both tagged and untagged types. Requiring the explicit declaration of overriding is used in some other programming languages, most notably Eiffel (which uses the keyword redefine). Other commonly used languages (including Java and C++) also have the same problem, but do not have the tradition of safety that Ada has. This additional checking still could allow an error to occur. The problem could still occur if there existed two or more overloaded inherited routines, and the user overrode the wrong one. However, this would be extremely rare, as it is normally the case that all of the inherited routines with a single name are overridden as a group. Subtle situations occur with generics and private types. We will consider generics first. The pragma Explicit_Overriding and the "overriding" syntax are checked in a generic_declaration (as well as in instantiations). Consider generic type GT is tagged private; package Gen is type DGT is new GT with private; overriding procedure Op (Z : DGT); -- Error: Doesn't override anything. private -- ... end Gen; If we didn't enforce the rule in generic_declarations (but only in instantiations), we could give "overriding" in this generic as shown. The effect would be to force all actual types for GT to have an operation Op. This would effectively modify the contract of the generic to include that operation. Of course, we could have said that the overriding checks are not applied in generic declarations so that the user can modify the contract in this way but this would have been at variance with the general language principle of applying all checks in a generic declaration and then checking again as necessary for each instantiation. As a consequence "overriding" can never be used on operations of a derivation of a generic formal private type (as in the example), as no primitive operations are inherited from the formal type. Thus we write "overriding or not" so that the instantiation can allow overriding. Thus generic type GT is tagged private; package Gen is type DGT is new GT with private; overriding or not procedure Op (Z : DGT); -- OK: no checking done. -- can be instantiated with a type that -- does or does not already have Op private -- ... end Gen; Observe that if no overriding indicator is given then the contract is changed since an instantiation which does indeed already have Op would be illegal. Thus generic type GT is tagged private; package Gen is type DGT is new GT with private; procedure Op (Z : DGT); -- No overriding indicator -- cannot be instantiated with a type that -- already has Op private -- ... end Gen; Note therefore that we can modify the contract to prevent overriding but not to require it. A related situation is where the formal type is derived so that there may be a number of known primitive operations. Consider generic type GT is new T with private; package Gen is type DGT is new GT with private; overriding procedure Op (Z : DGT); -- OK if T has Op as an operation. private -- ... end Gen; If the type T has Op as an operation then we must give an overriding indicator (either "overriding" or "overriding or not"). The form "overriding or not" would be appropriate for the situation where T did not have Op as a primitive but that DT derived from T does have Op and the generic is instantiated with DT. Note that the only check required at instantiation is to recheck operations which have neither "overriding" nor "overriding or not" to ensure that they do not override some inherited operation. The check in the generic_declaration is sufficient for "overriding", while "overriding or not" needs no check at all. A somewhat similar situation arises with private types. Thus consider package P is type T is private; function "+" (Left, Right: T) return T; private type T is range 0 .. 100; -- "+" overrides end P; as opposed to package P is type T is private; function "+" (Left, Right: T) return T; private type T is new TT; -- "+" does not override end P; The point is that the partial view does not reveal whether overriding occurs or not - nor should it since either implementation ought to be acceptable. We should therefore remain silent regarding overriding in the partial view. In order to permit checking when the full circumstances are known, we introduce the possibility of an overriding clause in the private part. This takes the form of an overriding indicator followed by a repetition of the subprogram specification whose profile has to be fully conformant with the profile given in the private view. An overriding clause is only necessary when the operation in the visible part does not have the overriding indicator "overriding" and in the full view the operation does override. Thus package P is type T is private; function "+" (Left, Right: T) return T; -- overriding not visible private type T is range 0 .. 100; overriding function "+" (Left, Right: T) return T; -- now we know end P; Any overriding clause must occur before the type is frozen. Note that overriding is always checked for both the partial view and the full view. The form "overriding or not" is permitted for both views. An overriding clause can always be given even if not necessary. Note that full conformance is required between the profiles although it is not strictly necessary for identification purposes. However, permitting a lesser level might be confusing for the reader. Note moreover that the conformance rules required for overriding are different for tagged and untagged types (tagged require subtype conformance whereas untagged only require type conformance) so it seems a good idea to overkill with full conformance for both. An overriding clause may not be given in the case where the operation is provided by a generic instantiation. This is because conformance rules are not defined for the profile of an instantiation. There are other restrictions on generic instantiations such as that they are not allowed to complete a subprogram declaration and so this restriction should not seem surprising. As a consequence an instantiation cannot be used to provide an operation in the visible part if that operation turns out to be an overloading one and this is not so for the partial view. This is probably not a frequent situation. The workaround is to give the instantiation a different name and then to rename it as the primitive operation thus package P is type T is private; procedure Pup is new Pap(P1 => A1, P2 => A2); procedure Op(Y: T) renames Pup; private type T is new TT; -- TT has an Op and so it overrides overriding procedure Op(Y: T); -- the overriding clause on the renaming end P; Observe that if the partial view says "overriding" then it would be a burden to require an overriding clause in the full view as well (although it is permitted). However, if the partial view says "overriding or not" then the full view needs to be checked still and thus might require an overriding clause. Similar examples occur with private extensions package P is type T is new T1 with private; procedure Foo; private type T is new T2 with ...; overriding procedure Foo; end P; This covers the situation where T1 does not have the operation Foo but T2 (which of course is ultimately derived from T1) does have the operation. If T1 does have the operation Foo, then an overriding indicator must be given for the partial view. An overriding indicator can be given for the full view but is not necessary. Thus package P is type T is new T1 with private; overriding procedure Foo; -- partial view is overriding private type T is new T2 with ...; overriding procedure Foo; -- unnecessary but harmless overriding clause end P; Implementing the checking of overriding takes some care, because an operation may become overriding at a later point (because of the rules of 7.3.1(6)). A compiler always knows that such overriding will happen later, so this should not provide an implementation burden. An example of how this could happen: pragma Explicit_Overriding; package P is type T is tagged private; private type T is ... procedure Op (Y : T); end P; package P.C is type NT is new T with null record; overriding procedure Op (Y : NT); -- OK. package Nested is type NNT is new T with null record; procedure Op (Y : NNT); -- Illegal because the body of Nested has -- visibility on primitive 'Op' -- We must add "overriding" -- or "overriding or not". end Nested; private -- P.C.Op overrides P.Op here (P.Op is inherited here). end P.C; package body P.C is package body Nested is -- P.C.Nested.Op overrides P.Op here (P.Op is inherited here). end Nested; end P.C; In this situation, the types NT and NNT are not private types and so there is no possibility of an overriding clause. Thus even though at the places where they are declared, the Op of T is not visible nevertheless the eventual overriding has to be mentioned. But we can write "overriding or not" as indicated in the comment to avoid breaking the abstraction (privateness) of the original type P.T. Contrast this with package P.C2 is type NT is new T with private; overriding procedure Op (Y : NT); -- Illegal, this doesn't override anything. package Nested is type NNT is new T with private; procedure Op (Y : NNT); -- OK. private type NNT is new T with null record; -- Error: We need an overriding clause for Op here with -- "overriding" or "overriding or not" end Nested; private type NT is new T with null record; -- P.C.Op overrides P.Op here (P.Op is inherited here). -- Thus, we need "overriding procedure Op (Y : NT);" here. end P.C2; Here the types NT and NNT are private types and so overriding clauses are required. The configuration pragma Explicit_Overriding does not necessarily apply to the entire partition. It can be given in front of individual units in order to apply only to those units. This allows subsystems which take advantage of the new syntax to be mixed with those that do not. In a mixed environment, it is important that units be checked based on the environment for which they were designed. Thus, generic instantiations are checked based on whether pragma Explicit_Overriding applies to the generic unit, and not whether it applies to the instantiation. We considered using further pragmas rather than syntax since syntax was originally considered to be too heavyweight a solution for this problem. However, the introduction of unreserved keywords makes the use of syntax much more attractive than if the words had to be reserved. An advantage over pragmas is that the syntax is clearly part of the declaration whereas pragmas are not. In the pragma approach the pragmas had to immediately follow the declaration (with optional inclusion of the subprogram identifier) and required special rules for the pragmas to ensure that they only applied to the immediately preceding declaration. The normal rules would have been that the pragma applied to all overloaded declarations with the same identifier which would clearly be inappropriate. There are also dangers if the pragmas get separated from the declaration during program amendment if the identifier is not included and including it seems a burden. We also considered various other forms of syntax where the indicator followed the details of the subprogram such as procedure Op(X: T) overrides is abstract; procedure Pop(Y: TT) overrides renames Pip; procedure Pup overrides is new Pap(P1 => A1, P2 => A2); However, it proved difficult to resolve the different grammatical forms and the need to indicate optional overriding as well. Placing the indicator at the front has the merit of being uniform and easily seen by the reader. The keyword "overriding" seems natural but the somewhat whimsical reuse of existing reserved words in "overriding or not" is perhaps unusual. No better alternative has presented itself and the optional form will not be that frequent. Other possibilities which were rejected included "maybe overriding", "possibly overriding", "overriding optional", and "overriding unknown". !corrigendum 8.3(26) To be done. !appendix At the Cupertino and Vienna meeting, John Barnes indicated that he would prefer this AI to use keywords. It was suggested that he prepare an alternative AI using keywords. (John often wishes that he had kept his mouth shut.) ************************************************************* From: Tucker Taft Sent: Wednesday, Marh 5, 2003 3:55 PM I notice in the minutes that in cases where you have to give the overriding clause in the private part for an operation declared in the visible part, we run into trouble with defining full conformance if the overriding declaration involves an instantiation. It seems easy enough to outlaw that, since we outlaw it in cases where we allow renaming-as-body, which has similar conformance problems. Why not just require that the instantiation have a new name, and then use an overriding, renaming-as-body to hook it up to the visibly declared operation? In Ada 95 we didn't allow instantiations to "complete" a subprogram declaration both because of the tricky conformance issues, and because of the possible confusion in trying to match up an instantiation with a separate declaration, since the instantiation doesn't have any explicit parameter profile. Hence, it would seem just as reasonable to disallow using instantiations with an "overriding" prefix under the same circumstances. ************************************************************* Editor's note: The following originally was a private discussion between John Barnes, Pascal Leroy, and me (Randy Brukardt) about a draft of version 4 of this AI. We started discussing a number of deep issues about this AI, and eventually decided that the discussion should be recorded for posterity. I've edited out private chit-chat and irrelevancies. And I've kept the threads together, which is not necessarily the order that items actually were discussed. The discussion took place between April 22 and April 26 2003. ---- 1st thread ---- Randy Brukardt John asked > The minutes of the last meeting say "Besides, if the syntax > is given, it is always checked. The configuration pragma > only changes the checking that occurs when the syntax is not > used." > > Did we really agree this? Yes. > I thought that the pragma had to > be on for the checking to be invoked. That is how the AI > currently reads. But, clearly we need to permit the syntax > always and so I suppose we had better check it because it > would be confusing not to. Exactly. It would be very strange if "overriding procedure" didn't have to override something. If the AI doesn't currently say that, its wrong. (Gee, that was easy. :-) > And then > > 2 > > Are we going to permit the "optional overriding" indicator > everywhere or only in those places where it is necessary (in > generic specs)? Currently it is permitted everywhere and > just effectively switches the checking off. But is this > useful? Note that "optional overriding" is not the > syntactic form I intend to propose. I will keep that secret > for a moment. Note that the summary of the minutes say > "Don't require maybe overriding on partial view". Maybe they > should have said "Don't permit maybe overriding on partial > view". I don't think that was the intent. Trying to figure out where something should be (or should not be) allowed seems like a fools game to me. People hate the places where Ada does that (in out parameters on functions are the prime example), and I don't think we ought to add more of them absent very good reasons. My model of these indicators on the partial view is that they ought to reflect the reality of the partial view. That is, if "overriding" is given, it better be a visibly overriding routine. But that may be too much checking and/or too hard to define, and certainly it is not needed. In any case, prohibiting "optional overriding" on a partial view wouldn't make sense (what if that partial view is in a generic spec?? Do you really insist to repeat the indicator in the private part, even though it just says 'don't check'??). In any case, one model of the absence of the configuration pragma is simply that all subprograms without an indicator have an implicit "optional overriding". Which only makes sense if you allow it everywhere. ---- Pascal Leroy I don't quite understand Randy's replies to these questions. It seems to me that "optional overriding" should only be allowed in generic specs, and that it should not be allowed on partial views. That seems to be the essence of the discussion as reflected in the minutes and as I remember it. ---- Randy Brukardt No, the minutes say "don't REQUIRE "maybe overriding" on the partial view". That's because John's previous version did require that on the partial view if you didn't want to break privateness. I don't think we discussed the notion of restricting "optional overriding" at the meeting; certainly *I* wasn't discussing that! I was only discussing the notion of requiring its use in a partial view where you would give the real overriding or not in the private part. I see no reason to restrict the use of modifier; that seems like a methodological restriction best left to style guides, not the language. Where such things were done in Ada, we've had continuing controversy (see: function parameter modes, 'in out'). Let's not go there again. But that's my opinion; I'm certain we've never discussed this topic at the ARG level (and perhaps we should). ---- Pascal Leroy I disagree here, but surely this should be discussed during a meeting, not between the three of us. Adding "overriding or not" outside a generic has no possible usefulness, except that of working around the constraints imposed by pragma Explicit_Overriding. As a programmer, you are always able to decide if a declaration is an override or not. The only case where you would want to use "overriding or not" is if you are too lazy to determine if a declaration is an override or not. I don't think we want to add language support for laziness. ---- End 1st thread. ---- 2nd thread: ---- John Barnes I ... found this suggestion from Tucker to get around the conformance with instantiation problem. Good idea? [Editor's note: This is Tucker's mail of March 5, 2003, appended above.] ---- Randy Brukardt It seems reasonable to me. I don't know if people would complain, but it at least avoids the hand-waving in the wording. ---- Pascal Leroy This certainly has the advantage that you don't have to invent complicated wording to define full conformance with instantiations. On the other hand, it would make it harder to change existing code to take advantage of AI 218, as you would have to introduce extra renamings. I am not sure how often the overrider is an instantiation; although this is certainly going to depend on the programming style, I suppose that this is going to be rather infrequent in practice. Even if we ignore for a moment the complexity of the RM wording for defining full conformance with an instantiation, I have a hunch that the rules are going to be sufficiently convoluted that they would lead to obscure errors and user puzzlement (of course, I would need to see the rules to be sure, but remember that full conformance in Ada 83 did puzzle many users because of unnecessarily stringent requirements). So I guess I am saying that I am slightly in favor of following Tuck's suggestion. However, in order to make an informed choice, I would like to see at least a first-order approximation of the rules for full conformance with an instantiation (maybe my gut feeling is wrong and they are not that complicated after all). ---- John Barnes Of course the other irritating thing with this conformance business is that overriding of tagged types requires type conformance whereas that for untagged types only requires subtype conformance. But I think we should use full conformance because it will look very odd if the overriding clause uses different names for the parameters. ---- End 2nd thread. ---- 3rd thread: ---- John Barnes Here is a tentative attempt at a rewrite of 218. ---- Randy Brukardt OK, here goes. The last paragraph of the !proposal seems awkward, although I think it has the right meaning. Perhaps "even in the absence of the pragma Explicit_Overriding" should be put into parens ("always made" certainly includes this) - certainly it reads a lot better without the "even in..." phrase (but I think its necessary, since its the point). 8.3(23.1) first sentence says: If an explicit declaration of a visible operation of a private type by a subprogram_declaration, abstract_subprogram_declaration, subprogram_ renaming_declaration or generic_instantiation includes an overriding_ indicator then it is permitted to give an overriding_clause for the operation in the corresponding private part. Shouldn't this say "does not include" instead of "includes"? I don't see any point to having: overriding or not procedure foobar... private overriding procedure foobar... 8.3(26) first question: Legality rules only apply in the visible specification of a generic instance unless the language says otherwise. Here, we clearly want these checks to occur in the private part as well (it makes no sense to ignore overridding indicators in generic instantiations). This check is not made in a generic instance body, so that generic sharing is still possible and that the contract is at least solely in the generic specification. (Overriding in bodies is rare, anyway). And the wording is correct. !discussion, first question. I'm pretty sure that we don't want to bother with the syntax on bodies for an obscure case. Indeed, people probably would prefer an error in that case, because it's unlikely that they actually meant to override anything. I'm not certain that I believe the explanation (you'll probably tell me that I wrote it originally); I'd probably just say that the value of supporting this new syntax on bodies is minimal (since you can always give an appropriate specification), and it is more likely that any overriding in a body is accidental rather than intentional. And I can't comment on buggering your book. :-) The 'later overridding' discussion. I'm not sure that the current wording (8.3(26.1) and 8.3(26.6)) has this effect. It's not clear when "an overriding declaration for the full view of the type" is determined. We want "overriding anywhere" informally, but the ARG has not liked such wording in the past. Secondly, the first sentence says "shall not be an overriding declaration for the view of the type at that place." That rule applies to types without partial views; but really we want that to apply only for partial views; for other types we want to use the second part of the rule (the one about full views). I don't think we have a choice but to use this interpretation for full types. Otherwise, the visibility gets very messy, and you'd have to allow additional overridding clauses anywhere. OTOH, the fact that the check is different (using a partial place rather than anywhere) for partial views is uncomfortable. (BTW: T is tagged, so NT and NNT need "with null record"). If we had P.C2: package P.C2 is type NT is new T with private; overriding procedure Op (Y : NT); -- Illegal, this doesn't override anything. package Nested is type NNT is new T with private; procedure Op (Y : NNT); -- OK. private type NNT is new T with null record; -- Error: We need "overridding" or "overridding or not" procedure Op here. -- That's because the body of Nested has visibility on primitive 'Op' end Nested; private type NT is new T with null record; -- P.C.Op overrides P.Op here (P.Op is inherited here). -- Thus, we need "overriding procedure Op (Y : NT);" here. end P.C2; I'm not expecting Mr. Private to be happy with this, but he'll need a better idea (and wording for it!) in order to change it. And this is a lot more important than adding restrictions to "overrides or not". Generic paragraph question: Yes. :-) It is OK. Second generic question: Yup. I always preferred allowing the contract to be modified in this way. It seems to be a useful capability. Moreover, if we allowed that, arguably, we wouldn't need "overriding or not". (I don't think it makes sense for a generic to be bisexual: both allowing and not allowing overriding. Generally, you do not want overriding in a generic, certainly not for mix-in uses.) ?? I concluded that if the partial view said "overriding" then it would be a burden to require it in the full view as well. Yes, and useless noise to boot. Why be required to repeat something obvious?? ?? One might argue that if the partial view said "overriding or not" then the full view need not be checked?? Yes, I expect that to be the semantics. Of course, Pascal doesn't want to allow it in that case, which also would answer the question. The generic mixin case should that we indeed have to allow "overriding or not" on a partial view. The good news is that the example is bogus, for two reasons: "overridding or not" on the partial view should extent to the private part, so no overridding clause is required (but its allowed). More importantly, where is PDGT coming from?? There is no such type in the spec. of the generic, and any outside type wouldn't match the generic formal. You can't construct such an example with formal private types. We could try: type GT is new T with private; type PDGT is new T with private; but I don't know if the derived declaration is legal. If the check is assume-the-worst, it fails; if it is assume-the-best, it is allowed. The wording of the language suggests that it is a hole - 7.3(8) is a legality rule that applies only in the visible part of an instance (that's the default). The second sentence seems to need to apply to the entire rule, not just "the requirement that the type is specific". ---- Pascal Leroy Good job, John, but I think there are a number of nasty issues that remain to be considered (that's the problem when proposals become more precise, you can write examples and ask "what if" questions). My comments interspersed below. Pascal -- > In 2.9(3) as modified by AI 284 add the following to the list of > nonreserved keywords > > overriding > > Change the syntax in 6.1 to read > overriding_indicator ::= overriding [or not] > subprogram_declaration ::= > [overriding_indicator] subprogram_specification ; > abstract_subprogram_declaration ::= > [overriding_indicator] subprogram_specification is abstract; > overriding_clause ::= overriding_indicator subprogram_specification ; I like the syntax. In fact, this is the first time that I see a syntax that I like for the "optional" case. Although it has a Norm Cohen flavor, it reads well, especially considering that it's on a line of its own. > Its profile shall be > fully conformant with the profile of the subprogram_declaration, > abstract_subprogram_declaration or subprogram_renaming_declaration > or the profile implied by the generic_instantiation. Intense hand-waving in the last part of this sentence. I think you'll need more words than that. The problem is that you are requiring full conformance (which is approximately lexical matching) but the generic instantiation model in 12.3(13-14) kills you. Consider: generic type T is private; procedure P (X : T); procedure I is new P (Integer); According to 12.3(13) the profile of the instance is: procedure I (X : T) and 12.3(14) explains that in this context the name T denotes the subtype Integer. There is no way that the user can write a subprogram specification that is fully conformant with this profile. (It's really a technicality, in the sense that there is no doubt what the right answer is here, but writing the words is going to be challenging.) > In addition to the places where Legality Rules > normally apply, these rule also applies in the private part of an > instance of a generic unit. > > ?? What's that last bit for Randy? Is it still OK??? I don't think it's for Randy. I believe it only says that all this overriding checking happens in the private part as well as the visible part. It would be silly if we made visible parts "safe" and private parts "unsafe". (Or am I missing something?) This leads me to wonder what happens in a generic body if you derive from a formal type. Surely we don't want the legality of the instantiation to depend on the contents of the body. It seems that the AI ought to say a few well-chosen words about this (I suppose we need some form of assume-the-worst rule). > package P.C is > type NT is new T; > overriding > procedure Op (Y : NT); -- OK. > package Nested is > type NNT is new T; > procedure Op (Y : NNT); -- Illegal because the body of Nested has > -- visibility on primitive 'Op' > -- We must add "overriding" > -- or "overriding or not". > end Nested; > private > -- P.C.Op overrides P.Op here (P.Op is inherited here). > end P.C; > > package body P.C is > package body Nested is > -- P.C.Nested.Op overrides P.Op here (P.Op is inherited here). > end Nested; > end P.C; > > Note the possibility of using "overriding or not" in this example as > indicated in the comment; it can be used here to avoid breaking the > abstraction (privateness) of the original type P.T. > > ?? Is Mr Private happy with this?? Mr Privacy is confused. My understanding is that you never have to add "overriding or not" to a declaration (except in generics). In this case I would expect P.C.Nested.Op to have no overriding_indicator, and the body to contain an overriding_clause. That would reflect the fact that Op doesn't override at the point of its declaration, and it wouldn't break the privacy of P.T. Hmm, but we don't allow an overriding_clause in a body, because it must occur before the freezing point. Why this rule about freezing? Note that, contrary to what the comment seems to imply, adding "overriding" to Op would break the privacy of P.T. I guess this example needs more thought. > ?? And so it seems that the earlier remarks that we didn't want to modify > the contract rules are not right. We can modify the contract to prevent > overriding but not to require it. Tis an odd business chaps ?? That seems quite broken. It appears to violate the Great Cosmic Duality between generics and (normal) private types. Consider your example: package P is type T is private; function "+" (Left, Right: T) return T; -- don't know here private type T is range 0 .. 100; overriding function "+" (Left, Right: T) return T; -- now we know end P; As the comment correctly points out, the absence of an overriding_indicator on the partial view really means "heck I don't know for sure, but so far it doesn't override" and the overriding_clause then tells the truth when we know it. Now look at the generic equivalent: generic type T is private; package G is type NT is new T; function "+" (Left, Right : T) return T; end G; It would seem to me that the natural application of the Great Cosmic Duality would be to say that the absence of an overriding_indicator on G."+" really means "don't know" and that the instantiation ought to say the truth by using overriding_clauses: type T is range 0..100; package I is new G(T) with overriding function "+" (Left, Right : T) return T; end I; -- John to find a good syntax And, oh, we wouldn't need "overriding or not" anymore because the instantiation is the one who would say the truth. (Sorry for the "stream of consciousness" style here. I am just inventing language as I type. The proposed solution may be totally bogus, but I think the examples in the AI indicate a deep problem.) ---- Randy Brukardt: Pascal said: > This leads me to wonder what happens in a generic body if you derive > from a formal type. Surely we don't want the legality of the > instantiation to depend on the contents of the body. It seems that the > AI ought to say a few well-chosen words about this (I suppose we need > some form of assume-the-worst rule). I don't believe that we can do assume-the-worst on this rule, since it is an either-or rule. If we did, we'd reject virtually all generic bodies containing a derivation (any instantiation MIGHT have an overridding routine) - the only ones that could work would be those for which all primitive routines have an overridding indicator (usually "overriding or not"). Since the most important part of the rule is checked in the generic body anyway (that intentional "overridding" really is), the only check that would not get made in a generic instance body is the "accidential overridding" one. That doesn't seem too bad, especially as derivations in bodies are rare. An AARM note about this probably would be worth having. (Indeed, a lot of the !discussion probably ought to be in the AARM.) > > package P.C is > > type NT is new T; > > overriding > > procedure Op (Y : NT); -- OK. > > package Nested is > > type NNT is new T; > > procedure Op (Y : NNT); -- Illegal because the body of Nested > > -- has visibility on primitive 'Op' > > -- We must add "overriding" > > -- or "overriding or not". > > end Nested; > > private > > -- P.C.Op overrides P.Op here (P.Op is inherited here). > > end P.C; > > > > package body P.C is > > package body Nested is > > -- P.C.Nested.Op overrides P.Op here (P.Op is inherited here). > > end Nested; > > end P.C; > > > > Note the possibility of using "overriding or not" in this example as > > indicated in the comment; it can be used here to avoid breaking the > > abstraction (privateness) of the original type P.T. > > > > ?? Is Mr Private happy with this?? > > Mr Privacy is confused. My understanding is that you never have to add > "overriding or not" to a declaration (except in generics). You never HAVE to; I think this example shows why you should be allowed to. > In this case > I would expect P.C.Nested.Op to have no overriding_indicator, and the > body to contain an overriding_clause. That would reflect the fact that > Op doesn't override at the point of its declaration, and it wouldn't > break the privacy of P.T. > > Hmm, but we don't allow an overriding_clause in a body, because it must > occur before the freezing point. Why this rule about freezing? So that there is a clear point at which the checking can be done. We need to know when the compiler has to go back and check all of the partial view subprograms that don't have overridding_clauses or an overriding_indicator. If you allowed overriding_clauses to happen in the body, you'd have to defer that check until you completed the compilation of the body. Which can be days or months later. I don't think we want an error in the spec. of a routine to fester until someone happens to compile a body. And then you'd have to somehow figure out when there was no body in order to do the check at the end of the spec. > Note that, contrary to what the comment seems to imply, adding > "overriding" to Op would break the privacy of P.T. > > I guess this example needs more thought. Yes; the compiler always knows that the routine is overriding, but supposedly the programmer does not. I think that this example really shows why you have to have "overridding or not" everywhere, because it is the only way to avoid breaking privacy in some cases. Note, however, that this is a dubious design. Usually, if you are intentionally overridding the routine, you would want to continue to do so in the private part (the routine ought to stay private). And most types ought to be private anyway; note that my similar example (which uses private types) has no problems with breaking privateness for NT. If you accidentially are overridding the private routine, that is exactly the case where trouble can happen and exactly what we're trying to detect. You really do need to break privateness enough to realize that "Op" is a bad name for this operation if you aren't intending to override the private. So I have to think that breaking privateness is inherent in the check. (Which is too bad.) > > ?? And so it seems that the earlier remarks that we didn't want to modify > > the contract rules are not right. We can modify the contract to prevent > > overriding but not to require it. Tis an odd business chaps ?? > > That seems quite broken. It appears to violate the Great Cosmic Duality > between generics and (normal) private types. Consider your example: That duality really doesn't exist in Ada 95 as it is (the sharing enabling rules among others see to that), and if we adopt "limited with", it will be completely dead (since "limited with" doesn't work at all with instantiations, meaning that it is useless if some of the types are declared in generics). So I think trying to preserve it here is questionable at best. Your two examples don't even look similar; I have a hard time believing anyone will be confused. > It would seem to me that the natural application of the Great Cosmic > Duality would be to say that the absence of an overriding_indicator on > G."+" really means "don't know" and that the instantiation ought to say > the truth by using overriding_clauses: > > type T is range 0..100; > > package I is new G(T) with > overriding > function "+" (Left, Right : T) return T; > end I; -- John to find a good syntax > > And, oh, we wouldn't need "overriding or not" anymore because the > instantiation is the one who would say the truth. If you want to kill this idea, this is a wonderful way to do it. The last time I was working on 218, I pretty much concluded it was intractable and that the idea had to be abandoned. John managed to rescue it with "overridding_clause"s, and we now seem to be near a solution. But if we start adding this to instantiations, the complexity factor has gone far beyond the benefits. I'm wondering personally if the idea of catching "accidential overriding" is worth the effort. If we stopped trying to do that (and left "no indicator" as "don't care"), it seems like most of these privateness problems (and the weirdness of the partial view rules) would go away. Come to think of it, why are we using a configuration pragma anymore? Once we have syntax, we can made the default to be "don't care" (that is, Ada 95 rules). Perhaps if we explicitly indicate the other cases: not overriding procedure Foobar (Pascal, John : ...); It's mildly annoying to have to write more to be safe, but then again that's what Ada's about. Then we don't have to try to describe where checking is done (it better be true at the point of the declaration of the overriding indicator); we don't have to describe where the pragma applies; and we probably could even allow overriding_clauses after freezing (because there is no rechecking - all checks occur at the point of the declaration/overriding_clause in question). One could imagine a tool or mode that tried to insure that all subprograms ultimately had an overriding_indicator or overriding_clause, but we could leave that for the style people and avoid having to worry about breaking privateness ourselves (if someone wants to break privateness to enforce a style, that's not our problem). Perhaps that should be an alternative #3??? (We need alternative #2 to prove why having the default be safe gets us into trouble. ---- Pascal Leroy > Since the most important part of the rule is checked in the > generic body anyway (that intentional "overridding" really > is), the only check that would not get made in a generic > instance body is the "accidential overridding" one. That > doesn't seem too bad, especially as derivations in bodies are rare. > > An AARM note about this probably would be worth having. > (Indeed, a lot of the !discussion probably ought to be in the AARM.) I'm not sure if we are discussing the same thing. The thing that bothers me is that, in John's current write-up, the overriding_indicator (or more precisely, the absence thereof) changes the contract. Consider the following (note that this is the untagged case, as I believe that in the tagged case 3.9.1(4) saves us): generic type GT is new T; package Gen is ... end Gen; package body Gen is type DGT is new GT; procedure Op (Z : DGT); end Gen; As I read the AI, it would seem that an instantiation of Gen is illegal if the actual type has an Op. At this point Mr. Shared Generic should have a heart attack. One way out of this conundrum is to say that in generic bodies, primitive operations of types that are derived from a formal type fall in the "don't know" category. Whether this has to be stated explicitly by writing "overriding or not" or whether this is the default (and "overriding" is illegal) is another issue. (Note that this would argue in favor of having "don't know" be the default, something you propose later on.) > > Mr Privacy is confused. My understanding is that you never have to > > add "overriding or not" to a declaration (except in generics). Well, the comment "We must add 'overriding' or 'overriding or not'" seems to imply the contrary. I believe that this comment is wrong anyway. > > Hmm, but we don't allow an overriding_clause in a body, because it > > must occur before the freezing point. Why this rule about freezing? > > So that there is a clear point at which the checking can be > done. We need to know when the compiler has to go back and > check all of the partial view subprograms that don't have > overridding_clauses or an overriding_indicator. If you > allowed overriding_clauses to happen in the body, you'd have > to defer that check until you completed the compilation of > the body. Which can be days or months later. Note that we have at least one case where this happens today, viz. the case of an incomplete type completed in the body, where you use 'Class in the spec. OK, this is particularly elegant language design, but at least that's a precedent ;-) My model is that the user writes on the declaration an overriding_indicator which reflects the situation at this place. If the situation later changes, he has to add an overriding_clause. If that's months later, fine, but at least it will be a conscious decision to override or not. The places where implicit declarations occur (and therefore where overriding occur) are very precisely defined by the RM, and these are the places where overriding_indicators/_clauses should occur. Let me come back to my generic example again: generic type T is private; package G is type NT is new T; function "+" (Left, Right : T) return T; end G; In the generic, "+" doesn't override anything. I am inclined to think that there is no point in saying "overriding or not", since there is no "+" in sight, and the situation is perfectly clear. Compare this with the case where T is a formal integer type, and where evidently "overriding" would be required. In the case of the formal private type, "+" may become overriding on the instantiation: type T is range 0..100; package I is new G(T); Here we have two options: 1 - Provide some syntax to specify that I."+" is overriding. This is the "safe" solution, as it makes possible to control overriding even in generics, but at the cost of possibly heavyweight syntax. 2 - Ignore the issue and say that it's OK for a declaration that was not overriding in the generic to become overriding in the instance. From the perspective of the instance, it is exactly as if you had "overriding or not". But from the perspective of the generic this is a bit safer as you have a way to express that "+" is not overriding (so if the formal part is later changed and T gets a primitive "+", you will be informed that there is potentially a problem). Am I saying that we don't need "overriding or not" after all? > If you want to kill this idea, this is a wonderful way to do > it. The last time I was working on 218, I pretty much > concluded it was intractable and that the idea had to be abandoned. > > John managed to rescue it with "overridding_clause"s, and we > now seem to be near a solution. But if we start adding this > to instantiations, the complexity factor has gone far beyond > the benefits. I don't think "kill" is a very productive word here. I was trying to look at the implications of the model, which is currently not satisfactory for generics. I am actually much more optimistic about this AI than I ever was. First, we have good syntax for the first time. Syntax is not nearly as important as semantics of course, but it's the first thing that people see, so it can be a stumbling block. Second, we have (with the overriding_clause) a good solution to deal with overriding occurring at diverse places in the program text. There are still issues to be resolved, notably for generics, but we are certainly starting to have a much deeper understanding of these issues. (Remember, this is an AI that was approved by WG9 two years ago, and we were entirely clueless about the problem then.) > Come to think of it, why are we using a configuration pragma > anymore? Once we have syntax, we can made the default to be > "don't care" (that is, Ada 95 rules). Perhaps if we > explicitly indicate the other cases: > > not overriding > procedure Foobar (Pascal, John : ...); I don't think changing the default simplifies the definitional problems with generics, private types, overriding occurring in odd places, etc. It just means that you have to write different combinations of keywords to achieve the same effect. This being said, this is an attractive idea. The pragma Explicit_Overriding always gave me bellyaches, so I'd be happy to get rid of it. For people who want to be safe and are not excited about writing their own ASIS tool, we could actually provide restrictions to control what checks ought to be performed: pragma Restrictions (No_Implicit_Overriding); pragma Restrictions (No_Implicit_Overloading); No_Implicit_Overriding would require that you write "overriding" when you override a subprogram. No_Implicit_Overloading would required that you write "not overriding" when you declare a new overload. Using the two restrictions together would require that you write either "overriding" or "not overriding" before each declaration. (And maybe "overriding or not" if we have to keep this one for generics.) > It's mildly annoying to have to write more to be safe, but > then again that's what Ada's about. That doesn't bother me as it would be easy to write a tool to add the overriding_indicators as appropriate (I could write such a tool in a couple of days). One could argue that all these overriding_indicators would degrade readability, but with the restrictions you could choose the trade-off between readability and safety. ---- Randy Brukardt: > > Since the most important part of the rule is checked in the > > generic body anyway (that intentional "overridding" really > > is), the only check that would not get made in a generic > > instance body is the "accidential overridding" one. That > > doesn't seem too bad, especially as derivations in bodies are rare. > > > > An AARM note about this probably would be worth having. > > (Indeed, a lot of the !discussion probably ought to be in the AARM.) > > I'm not sure if we are discussing the same thing. The thing > that bothers me > is that, in John's current write-up, the overriding_indicator (or more > precisely, the absence thereof) changes the contract. Consider the > following (note that this is the untagged case, as I believe > that in the > tagged case 3.9.1(4) saves us): > > generic > type GT is new T; > package Gen is > ... > end Gen; > > package body Gen is > type DGT is new GT; > procedure Op (Z : DGT); > end Gen; > > As I read the AI, it would seem that an instantiation of Gen > is illegal if > the actual type has an Op. At this point Mr. Shared Generic > should have a > heart attack. No, you're forgetting 12.3(11): legality rules aren't checked in an instance body. So there is no check, and the instantiation is legal. That IS what I was talking about. It's annoying to not have that check, but it follows automatically from the existing Ada rules. We probably ought to have a note, though, and your confusion shows why. > > > Hmm, but we don't allow an overriding_clause in a body, because it > > > must occur before the freezing point. Why this rule about freezing? > > > > So that there is a clear point at which the checking can be > > done. We need to know when the compiler has to go back and > > check all of the partial view subprograms that don't have > > overridding_clauses or an overriding_indicator. If you > > allowed overriding_clauses to happen in the body, you'd have > > to defer that check until you completed the compilation of > > the body. Which can be days or months later. > > Note that we have at least one case where this happens today, viz. the case > of an incomplete type completed in the body, where you use 'Class in the > spec. OK, this is particularly elegant language design, but at least that's > a precedent ;-) One that we're planning to make Obsolecent, simply because its such a bad design. Please, let's not make that mistake again... > My model is that the user writes on the declaration an overriding_indicator > which reflects the situation at this place. If the situation later changes, > he has to add an overriding_clause. If that's months later, fine, but at > least it will be a conscious decision to override or not. The places where > implicit declarations occur (and therefore where overriding occur) are very > precisely defined by the RM, and these are the places where > overriding_indicators/_clauses should occur. That's a fine model, but it will cause significant extra implementation effort. That's because the visibility of these things is a separate set of magic from determining whether they override or not (which uses visibility only to determine if it will never override - in large part because there is no visible semantic difference between overriding early or late - only between not overriding at all and overriding). That means any check such as you envision has to take into account a lot of visibility information which is unrelated to the overrides or not decision. And I'm convinced if you want to use that model, you have to use an explicit "not overriding" tag. > Let me come back to my generic example again: > > generic > type T is private; > package G is > type NT is new T; > function "+" (Left, Right : T) return T; > end G; > > In the generic, "+" doesn't override anything. I am inclined to think that > there is no point in saying "overriding or not", since there is no "+" in > sight, and the situation is perfectly clear. Compare this with the case > where T is a formal integer type, and where evidently "overriding" would be > required. > > In the case of the formal private type, "+" may become overriding on the > instantiation: > > type T is range 0..100; > package I is new G(T); > > Here we have two options: > > 1 - Provide some syntax to specify that I."+" is overriding. This is the > "safe" solution, as it makes possible to control overriding even in > generics, but at the cost of possibly heavyweight syntax. > > 2 - Ignore the issue and say that it's OK for a declaration that was not > overriding in the generic to become overriding in the instance. From the > perspective of the instance, it is exactly as if you had "overriding or > not". But from the perspective of the generic this is a bit safer as you > have a way to express that "+" is not overriding (so if the formal part is > later changed and T gets a primitive "+", you will be informed that there is > potentially a problem). > > Am I saying that we don't need "overriding or not" after all? Yes. 2 is essentially saying that the rules are not enforced in an instance at all. I don't view that as very safe. But I do not like either of these solutions. I'm in favor of solution 3 (at least for generic specs): 3 - Overriding indicators are part of the "contract". Thus, this instantiation is illegal - the rechecking of the instance spec fails. Note that my original proposal did NOT check these indicators (they were pragmas then) in the generic specification. The idea was that they WERE a first class part of the contract. Argubly, they should be up top with the formal types, but that seems way too heavy. > > If you want to kill this idea, this is a wonderful way to do > > it. The last time I was working on 218, I pretty much > > concluded it was intractable and that the idea had to be abandoned. > > > > John managed to rescue it with "overridding_clause"s, and we > > now seem to be near a solution. But if we start adding this > > to instantiations, the complexity factor has gone far beyond > > the benefits. > > I don't think "kill" is a very productive word here. I was trying to look > at the implications of the model, which is currently not satisfactory for > generics. I think it's fine, and your alternatives are worse (and I expect that my alternative will get little support as well). > I am actually much more optimistic about this AI than I ever was. First, we > have good syntax for the first time. Syntax is not nearly as important as > semantics of course, but it's the first thing that people see, so it can be > a stumbling block. Second, we have (with the overriding_clause) a good > solution to deal with overriding occurring at diverse places in the program > text. There are still issues to be resolved, notably for generics, but we > are certainly starting to have a much deeper understanding of these issues. > > (Remember, this is an AI that was approved by WG9 two years ago, and we were > entirely clueless about the problem then.) Agreed with that. One reason I never implemented it was that I couldn't figure out how to decide when a subprogram was "not overridden", because there was no "point" at which to check. I should have realized that that meant that there was a significant problem in the model. > > Come to think of it, why are we using a configuration pragma > > anymore? Once we have syntax, we can made the default to be > > "don't care" (that is, Ada 95 rules). Perhaps if we > > explicitly indicate the other cases: > > > > not overriding > > procedure Foobar (Pascal, John : ...); > > I don't think changing the default simplifies the definitional problems with > generics, private types, overriding occurring in odd places, etc. It just > means that you have to write different combinations of eywords to achieve > the same effect. Actually, it does, because it means that we can say that the check always occurs at the point of the overriding indicator. We can't do that now, because the absence of an indicator means that a check is done "sometime" that there is no overriding. All of your concerns with the placement of overriding_clauses stem from that, and all of that can be eliminated - along with the freezing requirement. (Although I guess with the existing model that you would prefer that the freezing requirement be eliminated and that all of the checks be repeated anytime that the visibility changes, which I think is silly.) Also, the "implicit" generic contract isn't quite so bad, because it is always spelled out with an overriding indicator. Admittedly, its still buried, but that is true of anything that causes an error in the assume-the-best-and-recheck-the-instance model -- such as deriving from a library-level tagged type and having to instantiate at library level. That's an "implied contract", and it is a far less explicit contract than this one would be. > This being said, this is an attractive idea. The pragma Explicit_Overriding > always gave me bellyaches, so I'd be happy to get rid of it. > > For people who want to be safe and are not excited about writing their own > ASIS tool, we could actually provide restrictions to control what checks > ought to be performed: > > pragma Restrictions (No_Implicit_Overriding); > pragma Restrictions (No_Implicit_Overloading); > > No_Implicit_Overriding would require that you write "overriding" when you > override a subprogram. No_Implicit_Overloading would required that you > write "not overriding" when you declare a new overload. Using the two > restrictions together would require that you write either "overriding" or > "not overriding" before each declaration. (And maybe "overriding or not" if > we have to keep this one for generics.) Not quite: a totally new identifier wouldn't be protected. Besides, don't you think that defining these properly would be as hard as defining Explicit_Overriding? The issues of where to check certainly occur here as well; it's precisely them that I want to get rid of. And certainly the generic issues would exist. We could informally suggest such things in the AARM, but I'm pretty sure we wouldn't want to try to define them. > > It's mildly annoying to have to write more to be safe, but > > then again that's what Ada's about. > > That doesn't bother me as it would be easy to write a tool to add the > overriding_indicators as appropriate (I could write such a tool in a couple > of days). One could argue that all these overriding_indicators would > degrade readability, but with the restrictions you could choose the > trade-off between readability and safety. Yes, its the readability issue that I think would be significant. Especially to anyone that hasn't struggled with the generic model. ----End 3rd thread. ----End private discussion. ************************************************************* From: John Barnes Sent: Wednesday, May 14, 2003 1:34 AM Attached is a rewrite of AI-218 on overriding. It captures a number of issues that I discussed with Randy and Pascal before revealing my inadequate understanding of Ada to the general public. I expect we will discuss it at length in Toulouse. [Editor's note: This is version /04 of the AI.] ************************************************************* From: Tucker Taft Sent: Wednesday, May 14, 2003 8:15 AM I don't remember talking about "overriding or not" as the syntax. It is a bit bizarre, but I'm sure Norm Cohen would love it, and I'm willing to play along. I note one "bug" in the discussion. Formal derived private types do have one operation, namely "=", and presumably overriding should be used with that one. ************************************************************* From: Pascal Leroy Sent: Wednesday, May 14, 2003 9:02 AM The minutes of the Padua meeting have: "We decide to put the keyword in front. John is asked to find a clever idea for the optional case." John tapped his indefatigable imagination and came up with this syntax for the optional case. I must say that I like it better than anything that has been proposed so far. ************************************************************* From: Robert Dewar Sent: Wednesday, May 14, 2003 9:08 AM Those were jokes from Norm! I dislike this kind of keyword joke, except as a joke. I can't believe that someone would seriously suggest such ugly abuse of OR or NOT ************************************************************* From: Tucker Taft Sent: Wednesday, May 14, 2003 10:50 AM I don't feel as strongly as Robert on this one, but here are some alternatives: allow overriding procedure blah(...); possible overriding procedure blah(...); potential overriding procedure blah(...); Of the three, I probably prefer "possible". "Allow" sounds like we are allowing overriding of this routine in subsequent descendants, rather than allowing this subprogram to be an overriding. I believe our latest thinking permits these only in the visible part of a generic package. One possibility is to simply relax the rules and say that no overriding check is performed on an instance, making "possible overriding" the default in a generic, in other words. A third possibility is to make "possible overriding" the default in a generic, but allow an explicit "not overriding" prefix if desired, which is the default everywhere else. In a generic, if given explicitly, "not overriding" means "not overriding even in the instance." E.g.: type NT is new formal_type [with ...]; not overriding procedure Blah(X : NT); means that Blah must not be overriding an operation that is inherited from formal_type, including in the instance. I suppose this third possibility is my mild favorite. It allows users to be explicit everywhere if they like, and only in a generic is there a third option, which is between overriding and not overriding, requiring the "not overriding" to be explicit. One reason I prefer this is that it seems to me an unusual generic that really *cares* whether it is overriding in the instance. I would rather not have to go and add "possible overriding" in all my generics, whereas I am quite happy to add "overriding" as necessary to be able to get additional checking. ************************************************************* From: Robert Dewar Sent: Wednesday, May 14, 2003 11:15 AM I must say that so far I preferred the pragma solution, I have no objection to a non-pragma solution if a decent syntax can be found, but so far I have not seen evidence of this. I also think a pragma solution is better because it allows earlier implementation, and also btter forwards/bacwards comatpiblity. ************************************************************* From: Tucker Taft Sent: Wednesday, May 14, 2003 11:52 AM I originally favored a pragma solution, but was convinced by how prevalent these annotations would be. Almost every dispatching operation that is not in the "root" type will need this annotation. That seems to make them inappropriate for a pragma, in my eyes, or at least a pragma having "good taste." ;-) ************************************************************* From: Randy Brukardt Sent: Wednesday, May 14, 2003 3:17 PM The real problem is that it isn't possible to solve the problem with pragmas alone, at least with the existing syntax of pragmas. John, Pascal, and I had a lengthy private discussion on this AI which we've since decided needed to be made public (since it covered a lot of annoying issues). One possibility which I will explore further is eliminating the pragmas and "optional overridding" altogether, by making the default be "don't care". This is slightly less safe, but it appears to be less complicated. In any case, this is a lot harder to do than it appears at first glance. Any solution works well in the 'typical' cases, but getting it right for generics and for private types with additional operations is hard. ************************************************************* From: Robert Dewar Sent: Wednesday, May 14, 2003 4:28 PM That seems amazing, because the only syntactic requirement for pragmas is that the arguments have the syntax of expressions (they can have any old seman6tics you want). ************************************************************* From: David Emery Sent: Wednesday, May 14, 2003 4:35 PM Yeah, but I remember that the general intent of pragma-hood is to limit the effects of a pragma on legality or language-specific semantics. Doing this kind of thing with a pragma strikes me as A Bad Thing. Also, it strikes me that "override" and its verb forms are likely to appear as identifiers in existing real program: if authorized or override then launch_the_missile; else beep_annoyingly_at_operator; end if; ************************************************************* From: Robert Dewar Sent: Wednesday, May 14, 2003 6:32 PM Lots of pragmas affect legality, here is a partial list elaborate elaborate_body elaborate_all inline import convention restrictions (!) pack preealborate pure well that's off the top of my head Many others affect language-specific semantics remote_types suppress atomic volatile c_pass_by_copy locking_policy discard_names again that's off the top of my head Actually it takes more work to think up a list of pragmas that do NOT affect either legality or run-time semantics. So there may be good arguments against the use of a pragma, but this is not one of them! ************************************************************* From: Gary Dismukes Sent: Wednesday, May 14, 2003 4:18 PM Tuck wrote: > I suppose this third possibility is my mild favorite. It allows > users to be explicit everywhere if they like, and only in a generic > is there a third option, which is between overriding and not overriding, > requiring the "not overriding" to be explicit. The idea of making "possible overriding" the default for the generic case is attractive. (Presumably this would only apply to primitives of types derived from formal types.) > One reason I prefer this is that it seems to me an unusual > generic that really *cares* whether it is overriding in > the instance. I would rather not have to go and add > "possible overriding" in all my generics, whereas I am > quite happy to add "overriding" as necessary to be able > to get additional checking. I agree, it seems like it would be really annoying to have to apply the "possible" indication to all primitives in a generic. The drawback is that it's a little uncomfortable to have different defaults for the generic vs. normal case and the inconsistency has the potential to cause confusion. However it does seem to make the feature easier to use. ************************************************************* From: Tucker Taft Sent: Wednesday, May 14, 2003 9:21 PM > The idea of making "possible overriding" the default for the > generic case is attractive. (Presumably this would only apply > to primitives of types derived from formal types.) I think it need only apply for the check in an instance. There need not be any change in the semantics when compiling the generic itself. Essentially, the default would be "this doesn't override any primitive in the generic, and I don't care whether it overrides any primitive in the instance." Explicitly saying "not overriding" would mean "this doesn't override any primitive in the generic, and it *also* doesn't override any primitive in the instance." The distinction between generic and instance only exists for a type derived directly or indirectly from a generic formal type, so the distinction between "" and "not overriding" only exists in a generic. In all other contexts, they mean the same thing. > I agree, it seems like it would be really annoying to have to > apply the "possible" indication to all primitives in a generic. > > The drawback is that it's a little uncomfortable to have different > defaults for the generic vs. normal case and the inconsistency > has the potential to cause confusion. However it does seem > to make the feature easier to use. If you take the approach I suggest above, the default is effectively equivalent to "not overriding" everywhere except in a generic, because only in a generic is there the distinction between the check on the generic and the check on the instance. ************************************************************* From: Gary Dismukes Sent: Thursday, May 15, 2003 12:47 AM Makes sense, thanks for the clarification. I'm in favor of this approach. ************************************************************* From: Robert I. Eachus Sent: Wednesday, May 14, 2003 9:05 PM First let me say that my strong preference is that the default is may override. If the keyword solution is preferred, for example, "overriding" means that this declaration must override, "not overriding" means that the subprogram must not override, and no keyword is status quo ante. So this eliminates the need for the environment pragma, and answers all the potential questions about mixing new and old code. Robert Dewar wrote: > So there may be good arguments against the use of a pragma, but this is > not one of them! In fact I think that if we go with the default as may or may not override, there is a huge benefit to a solution using pragmas. If the pragmas are present but the compiler used doesn't recognize the new pragmas? No problem. But the original proposal and variations requires an upwardly incompatible change. If the justification was strong, I might vote for a change that induced compatibility problems. Making the default may or may not and using pragmas is totally upwardly compatible. People who like the idea can start coding that way tomorrow. So I think that one of the major benefits of the pragma approach is this ability to start using the feature before compiler support is ready, and not have problems if, for example, your target compiler doesn't support the pragmas, but the compiler used for initial testing on the host does. You get the benefit of the pragmas during development, and no worries that the source has to be modified for the target compiler. One last point. There is a potential problem with the pragma approach when several declarations in the same declarative part have the same name. I think that the pragmas should apply to all declarations with the same name, as most of the time that is exactly what will be desired. (For example, if you have several Finalize operations declared in the same scope, it will be highly unusual to want only some of them to override.) But there needs to be a way to deal with the unusual case. I think that I prefer that the pragma normally apply to all declarations in scope. But if there are two or more pragmas applying to the same name, the first should apply to declarations before its point of occurance, and the second declaration, should apply up to the point of its occurance and so on. That way the pragma can be put directly after the declaration(s) it applies to. But in any case, you must have all three pragmas available (or a pragma with three arguments), independent of the default. I prefer pragmas Overrides, No_Overriding, and May_Override. ************************************************************* From: Tucker Taft Sent: Wednesday, May 14, 2003 9:26 PM Robert I. Eachus wrote: > First let me say that my strong preference is that the default is may > override. If the keyword solution is preferred, for example, > "overriding" means that this declaration must override, "not overriding" > means that the subprogram must not override, and no keyword is status > quo ante. So this eliminates the need for the environment pragma, and > answers all the potential questions about mixing new and old code. That is the default in the absence of the pragma. The whole point of the configuration pragma is to make the default "not overriding." So if you prefer the default being "may override" then you won't use the configuration pragma. But many people want the confidence that overriding happens only when explicitly requested. For them, the configuration pragma provides that. In fact, even if we didn't standardize it, I suspect the configuration pragma would be invented! It is clearly a useful guarantee to provide in some environments. As far as going back to pragmas, please believe that that ground has been well tilled, several times. Pragmas really don't work very well for various and sundry technical reasons, and having to use them pervasively I believe is a serious misuse of pragmas. You should definitely read the minutes from the past few meetings on this AI to fully understand the issues. ************************************************************* From: Robert I. Eachus Sent: Thursday, May 15, 2003 1:22 AM Tucker Taft wrote: > You should definitely read the minutes from the past few meetings > on this AI to fully understand the issues. Oh, I understand the issue all right. Let me sneak in one other advantage to pragmas, and then I will discuss why I think a pragma or pragmas is the right approach. If we use the pragma approach, then it is possible to have two variations which will make the pragmas much more useful. The first is that if the pragma does not contain a subprogram name, or a list of subprogram names if that is allowed, then the pragma should apply to all subprograms declared in the scope. This is often what you want if you are creating a descendent of a tagged type in a package. All of the subprograms declared in the scope are expected to be overloading. But there are also cases where most of the subprograms are explicit overloads, but there are one or two operations added for this (OO not Ada) subtype. So if the pragmas are allowed with no arguments, then a pragma with a specific argument will only apply to that name. Doing this would make using the feature much less obtrusive. It is possible to go further, and allow type names as parameters to the pragmas. This would mean that all operations of that type declared in this scope are expected to be overloading. (Or non-overloading depending on the pragma used, but for type names the first will be much more common.) Okay, now to raise the flaming sword and cry havoc! on the pragma vs (non-)reserved word issue. The purpose of these annotations (and whatever form they take, that is all they are) is to promote program correctness. A good and just cause. But if the feature is not used, it won't help. And even more important, if there are a mixture of usage styles in the wild, then distributing Ada components becomes much more difficult, you need one version that uses the feature and one that does not. Now let's look at the world where instead of on a per library basis, the feature is used on a per unit basis. Compilers can have all sorts of compilation unit pragmas, or more realistically compile time switches that control error and warning messages. On an individual preference basis we will probably see ignoring missing pragmas, and warnings for missing pragmas as the two favorites. Software development plans and style guides will probably require the pragmas to be present, even if you want the default (whatever that turns out to be). But in all compiler modes, if one of the pragmas is present but violated, that should be reported as an error. If this is the way the world ends up, then those who are willing to pay the minor overhead of using the pragmas will be happy. Development environments will have templates that supply the pragmas, and there will be tools to add the pragmas where they are missing. (Useful perhaps, for lazy programmers, but more useful for converting existing software to use the feature.) But all of these features will blunt the usefulness of the feature. (Notice that this can happen just as well with the non-pragma version.) What you really want is for the programmer to think, and tell the compiler what he thinks. That is why I think that allowing the type name as a pragma argument is such a win. When programmers declare a new derived tagged type, they will document their expectation--all overloading, no overloading, or some mix. It might be possible to change the language syntax to have a specific portion of the declarative part where overloading subprograms, and only overloading subprograms can appear. But I think that begins to look more like C++ than Ada. Pragmas that apply to all the operations of derived tagged types in the scope I think will make the cost of the feature small to the programmer, and the likelihood that the programmer will think while he is using it fairly high. Oh, one last point. Whether the pragma or non-reserved word sides win, the effect of applying the pragmas or non-reserved words to subprograms that are not operations of a derived tagged type should be an error outside of generics. (I can see cases where you want to use them in generics package specifications even when it is not clear to the compiler that the applicable generic formal type will be tagged. For generic formal packages, the compiler will know.) ************************************************************* From: Tucker Taft Sent: Thursday, May 15, 2003 8:25 AM The configuration pragma can be used library-wide, or on an individual source file (aka "compilation"). I think this gives the flexibility needed, while also allowing the project-wide control if desired. Robert I. Eachus wrote: > Tucker Taft wrote: > >> You should definitely read the minutes from the past few meetings >> on this AI to fully understand the issues. > > Oh, I understand the issue all right... If so, then you should address the specific issues in the minutes before launching off on other topics! ************************************************************* From: Robert I. Eachus Sent: Thursday, May 15, 2003 11:44 AM If I there is something missing in the draft minutes of the Padua meeting, you should probably suggest adding it. Other than that, I think the arguments are summed up in version 2 of the draft AI as: "An advantage over pragmas is that the syntax is clearly part of the declaration whereas pragmas are not. In the alternative approach the pragmas had to immediately follow the declaration (with optional inclusion of the subprogram identifier) and required special rules for the pragmas to ensure that they only applied to the immediately preceding declaration. The normal rules would have been that the pragma applied to all overloaded declarations with the same identifier which would clearly be inappropriate. There are also dangers if the pragmas get separated from the declaration during program amendment if the identifier is not included and including it seems a burden." I thought those were the issues I was addressing. The first statement is obvious and an argument for the syntax change which I accept. However, I think that the advantages of the all pragma approach outweigh the disadvantages. I don't really buy the argument about special rules for pragmas being needed. There will be cases where overriding and non-overriding subprograms with the same name make sense, for example, a Create operation. My proposal does address that case. The dangers mentioned in the last sentence I definitely disagree with. With the pragma solution my choice would be to place the pragmas directly after the derived type declaration: type Foo is new Ada.Finalization.Controlled with private; pragma Overrides(Initialize, Finalize); As long as there is a rule/understanding by compiler vendors that it is an error when there is no corresponding declaration, this placement is much more useful to the maintainer. As for the author, locating the pragma directly after the type declaration would be much more likely to mean that he thought about the overriding issue. Some bookkeeping if you will. Placing the prama(s) directly after the type declaration (or after the full declaration in the private part) argues strongly for the configuration pragma Explicit_Overriding with the currently documented meaning. That way you get some benefit from using the pragma Overrides even absent the Explicit_Overriding pragma, and the full benefit when the Explicit_Overriding changes the default from may override to does not override. You still need the non-overriding and may override pragmas in both cases, and a compiler option to warn about potentially overriding subprograms which are not covered by a pragma. This causes a slight problem with the Explicit_Overriding pragma because you really need two versions. My choice of how to do that would be to have two pragma arguments Strict and Mild, with Mild the default. Mild would mean that the compiler would only flag as errors cases where an overriding declaration was not flagged as such, the strict version would require that all such declarations be explicitly flagged as overriding, non-overriding or don't care. Back to my example above, I would expect the normal style under the strict version to be: type Foo is new Ada.Finalization.Controlled with private; pragma Overrides(Initialize, Finalize); pragma Non_Overriding(Foo); This would mean that Initialize and Finalize are overriden, and that there are other operations for Foo declared here that are non-overriding. Again I strongly feel that the best place for this information is right after the derived type declaration, or the full type declaration for private types where the full declaration is a derived type. There will be cases where this preferred location can't be used or doesn't make sense, but I feel that is a decision for the programmer, not the ARG. Imagine a ten page package declaration where there is an overriding of Finalize on page 6. That is at least as hard to find as the Finalise declaration used as an argument for this feature. If the pragmas are located just after the type declaration, that is going to make things much easier for the maintainer, or for someone writing a package that further derives from the type in the hypothetical ten-page package declaration. (Text_IO is only five.) Which brings up one last point. Are there any standard library packages that are going to need to change as a result of this AI? I can't think of any current packages, but there are some proposed new container packages that will. ************************************************************* From: Robert Dewar Sent: Thursday, May 15, 2003 1:43 PM > People who like the idea can start coding that way tomorrow. Also implementors who like the idea can implement it tomorrow :-) ************************************************************* From: Pascal Leroy Sent: Thursday, May 15, 2003 4:37 PM I am sick and tired of rehashing the same arguments over and over again, so let me quickly say this: We tried with pragmas. As a matter of fact, we wrote the AI with pragmas initially, for all the good reasons that were listed (again) in this thread. As a matter of fact, this AI went all the way up to WG9 and was approved. It only had one minor problem: it was all wrong. In order to deal with complicated situations involving private types, you need to be able to designate one particular subprogram among a set of overloaded subprograms. If you don't want to force users to add renamings all over the place, this means that you need a way to specify a subprogram parameter profile in a pragma, e.g.: pragma Foo (function F (X : Float) return Boolean); That's new syntax, so if we have to have new syntax we might as well do it right. See the end of the appendix of http://www.ada-auth.org/cgi-bin/cvsweb.cgi/AIs/AI-00218.TXT?rev=1.14 for details. ************************************************************* From: Randy Brukardt Sent: Thursday, May 15, 2003 7:43 PM Pascal said: > I am sick and tired of rehashing the same arguments over and > over again, so let me quickly say this: I had written essentially this same message when the power went off here, dumping it into the ether. Pascal's message had come before I redid it. One amplification to Pascal's message. Robert Eachus claims that you do not need separate identification of routines, having a simple one for all overloaded routines is sufficient. That is rubish. It is quite common in Claw for a derived type (subtype in O-O terms) to have routines with the same name and additional parameters. That's partially because of our minimalistic naming (we try to name routines "Add" as opposed to "Add_Key_to_List"). In such a case, usually the existing routine is overridden, and the new version with additional parameters is an overriding. A single pragma which specifies one or the other will not work in this case; using a pragma to specify "don't care" eliminates the added safety we're trying to gain. After all, this feature is intended to increase the safety of O-O programs by preventing unintended changes (the most important being when a routine which is intended to be overriding is not in fact overriding). If such safety requires the use of lots of extra declarations (such as renamings), or worse, changing the names of routines, it is not going to get used as much. After all, in a perfect world, all Ada code would use these "overridding indicators" on all subprogram declarations (which probably is how the language should have been designed in the first place - but this problem goes back to Ada 83 and before). Also, solving the problem by prohibiting these on subprograms in the visible part or some such doesn't make sense either, as most O-O types will be some sort of private type. I'm no longer convinced that an acceptable solution is possible for this problem (even using syntax); there's no hope for pragmas alone. ************************************************************* From: Robert Dewar Sent: Thursday, May 15, 2003 8:56 PM > That's new syntax, so if we have to have new syntax we might as well do > it right. Actually you do not have to add new syntax, as DEC showed with its import and export pragmas, it is perfectly possible to specify a specific overloading in a pragma. However, it's not very attractive, so I understand this argument, nevertheless I think pragmas are preferable. The point is that the argument: > That's new syntax, so if we have to have new syntax we might as well do > it right. is convincing but erroneous, since it rests on a false assumption. ************************************************************* From: Randy Brukardt Sent: Thursday, May 15, 2003 8:56 PM Fine. Feel free to make an update to the AI-00218.TXT proposal that covers all of the issues in the Appendix to John's recent proposal. I was the original author of the pragma proposal, and I will not work on it further (as author, of course I'll work on it as editor), as I believe that either John's proposal or the alternative that I've discussed privately are better solutions. The reasons are usability and implementability. Because the syntax is part of the subprogram declaration, the necessary checking can (indeed, must) be done when the declaration is processed. That means that all of the information is there to make the checking. With Janus/Ada, at least, we figure out the overridding (or not) when we process the declaration, assigning tag slots, then discard it all (its not stored). It can be figured out again later, but its painful to do (and error-prone). I also think it is more readable to use syntax for this, as it means that the specifiers can't get separated from the subprograms. The only question is whether syntax is too heavy. I don't think so, because this is a significant safety issue in Ada 95. Given that we're marketing Ada as having been designed to be safe, it is rather embarassing to have the 'wrong' routines to be called without being detected by the compiler. (The other such issue, default initialization of scalar and array objects, probably ought to be looked at again. But that is a case of something that was looked at in Ada 95 and explicitly decided not to fix. So I'm not sure of any benefit of reopening it.) ************************************************************* From: Pascal Leroy Sent: Friday, May 16, 2003 1:39 AM > Actually you do not have to add new syntax, as DEC showed with its import > and export pragmas, it is perfectly possible to specify a specific > overloading in a pragma. However, it's not very attractive, so I understand > this argument... One of the issues was that we didn't want to come up with something that would look bolted-on five years from now. Having to pepper a specification with complicated pragmas doesn't look too appealing now, it will be really ugly in 2008. Of course, I realize that this is an aesthetical argument. The most convincing argument in favor of pragmas is that you can start using them now, without compiler changes. However, I'm not sure I entirely buy this. Once we have agreed upon a syntax, it's an afternoon project for an implementer to add that new syntax (without any semantic checks) to their parser. And there are less that half-a-dozen front-end technologies these days. So if there is the slightest market demand for this, syntax support can be in compilers in no time (if there is no market demand the whole point is moot). For any real-life project, starting to use this feature would be a conscious decision, carefully planned, and they might as well ask their favorite vendor for a parser patch. ************************************************************* From: Pascal Leroy Sent: Friday, May 16, 2003 3:37 AM > Actually you do not have to add new syntax, as DEC showed with its import > and export pragmas, it is perfectly possible to specify a specific > overloading in a pragma. I must also mention that this approach, which worked fine in Ada 83, is not-so-straightforward in Ada 95 because of the addition of anonymous access types. The pragma: pragma Export_Function (Foo, Parameter_Types => Integer, access Boolean, Float, Result_Type => String); does not follow the syntax rules because 2.8(3) only has name or expression, not access_definition. I realize that you can invent a convention to handle this case, but it becomes quite distasteful. Plus, you have to remember that we consider adding anonymous access-to-subprogram types as part of the amendment, and these types would be intractable using a pragma without new syntax. ************************************************************* From: Robert I. Eachus Sent: Thursday, May 15, 2003 11:39 PM Randy Brukardt wrote: > Pascal said: >>We tried with pragmas. As a matter of fact, we wrote the AI with >>pragmas initially, for all the good reasons that were listed (again) in >>this thread. As a matter of fact, this AI went all the way up to WG9 >>and was approved. It only had one minor problem: it was all wrong. >> >>In order to deal with complicated situations involving private types, >>you need to be able to designate one particular subprogram among a set >>of overloaded subprograms. If you don't want to force users to add >>renamings all over the place, this means that you need a way >>to specify > One amplification to this. Robert Eachus claims that you do not need > separate identification of routines, having a simple one for all overloaded > routines is sufficient. That is rubish. It is quite common in Claw for a > derived type (subtype in O-O terms) to have routines with the same name and > additional parameters. I think you guys threw out the baby with the bathwater. I said in my initial message: "One last point. There is a potential problem with the pragma approach when several declarations in the same declarative part have the same name. I think that the pragmas should apply to all declarations with the same name, as most of the time that is exactly what will be desired. (For example, if you have several Finalize operations declared in the same scope, it will be highly unusual to want only some of them to override.) "But there needs to be a way to deal with the unusual case. I think that I prefer that the pragma normally apply to all declarations in scope. But if there are two or more pragmas applying to the same name, the first should apply to declarations before its point of occurance, and the second declaration, should apply up to the point of its occurance and so on. That way the pragma can be put directly after the declaration(s) it applies to. "But in any case, you must have all three pragmas available (or a pragma with three arguments), independent of the default. I prefer pragmas Overrides, No_Overriding, and May_Override." As far as I can see this solves all of the nasty examples that have been given. There are times when it looks ugly in some of the more bizzare cases, but if multiple pragmas with the same name as an argument always apply to all preceding subprogram declarations with that name it works. There are cases where this generalization of the must immediately follow rule makes the text elegant, and it is always possible to construct cases which require more than one pragma per explicit subprogam name. I consider those cases examples of bad design but that is a detail. Let me show you how the examples cited by Randy and Pascal work: It is quite common in Claw for a derived type (subtype in O-O terms) to have routines with the same name and additional parameters. That's partially because of our minimalistic naming (we try to name routines "Add" as opposed to "Add_Key_to_List"). type Widget is new ... with private; procedure Add(W: in out Widget;...); procedure Add(W: in out Widget;...); procedure Add(W: in out Widget;...); pragma Overrides(Add); procedure Add(W: in out Widget;...); procedure Add(W: in out Widget;...); procedure Add(W: in out Widget;...); pragma No_Overriding(Add); Is that so horrible? I could complain about all those Add operations, but that is your design choice. Incidently notice that at first it seems unAdalike to have pragmas whose scope ends at the pragma. But once you think about what these pragmas do, it makes perfect sense. These are assertions. They don't change anything about the declarations they apply to, they just check that the asserted behavior is true, and if strict checking applies, they note that these declarations are correct. My proposed rule allows the pragma to appear before the Overridings just to allow this case: type Foo is new Bar with private; pragma Overrides (Foo); This says that all of the subprogams which become operations of Foo, whether declared in the public part of a package or in the private part. It is legal, but from my perspective a bit naughty to do this, then in the private part say: private function My_Create(...) return Foo; pragma No_Overriding(My_Create); ... I think that many style guides would frown on that, but that is the purpose of style guides, to frown appropriately. ;-) One of the troubling cases in version 1.14 of the write-up is: package P is type T is tagged private; private procedure P (Y : T); end P; package P.C is type NT is new T; procedure P (Y : NT); pragma Overrides (P); -- Could use Might_Overrides here, -- so privateness is not broken. private -- Overrides here P. end P.C; You get to choose between lying publicly, or exposing private details. With my scheme, the appearance of the pragma can be delayed: package P is type T is tagged private; private procedure P (Y : T); end P; package P.C is type NT is new T; procedure P (Y : NT); -- No pragma here, so privateness is not broken. private pragma Overrides (P); -- In the private part, the -- private part of P is visible -- so pragma makes more sense here. end P.C; As for this example, I am on very shaky ground about what is wanted: pragma Explicit_Overridding; package P is type T is private; function "+" (Left, Right: T) return T; pragma Optional_Overriding("+"); -- So there is no check here. private type T is range 0 .. 100; -- "+" overrides function "+" (Left, Right: T) return T; -- A redeclaration. pragma Overrides ("+"); end P; Apparently the assumption is that overriding behavior must be specified even for types that are neither tagged types or derived types, and for implicitly declared operations. Also that there is some reason for explicitly lying in the public part of the package declaration. I don't agree with any of that. If you feel it is needed how do you do it with the changed syntax proposal? If I am missing the point of the example please explain it to me. Especially since Randy said: "The main advantage of the pragmas only proposal was that no new syntax or semantics (other than the check itself) is needed. That no longer appears to be the case, so I have reluctantly abandoned this alternative of the AI." But if I do understand the intentions of the example, with my solution you can put the pragma where it makes sense: pragma Explicit_Overridding; package P is type T is private; function "+" (Left, Right: T) return T; -- No pragma here. private type T is range 0 .. 100; -- "+" overrides -- No redeclaration here. pragma Overrides ("+"); -- applies to "+" in visible part end P; ************************************************************* From: Tucker Taft Sent: Friday, May 16, 2003 5:20 AM There was at least one other problem, which had to do with subprograms which were not overriding in the visible part, but which were overriding in the private part, although the subprogram was itself declared in the visible part. This was felt to be one of the final "nails" in the pragma "coffin," as it effectively *required* that the pragma be separated from the subprogram spec, making the "placement" approach to identifying one subprogram no longer sufficient. Although we were all aware that Dec/Compaq/HP came up with a way to allow pragmas to specify particular subprograms among a group of overloaded subprograms, we all felt that this problem ultimately *deserved* syntax. Having made that decision (now several meetings ago, though it was rehashed in Padua), we moved on to deciding on the best syntax. Although it is sometimes fruitful to re-open certain decisions if there are some new insights, I haven't heard anything sufficiently new here to justify re-opening this one. We really have gone through this decision process quite carefully, and we reached a consensus. I realize that if someone didn't attend an ARG meeting they couldn't contribute or not contribute to such a consensus, but that is the way these things tend to work. Sometimes when a decision is still tentative, it can be kept open long enough to allow non-attendees to make some important contributions, but at least for this one, that would have had to have been a while ago. Now I think we should focus on the syntax, and try to get it right, rather than swinging back around to the starting point. ************************************************************* From: Robert Dewar Sent: Friday, May 16, 2003 6:03 AM I must say I can't see any significant problem with Robert (Eachus) proposal, but perhaps I am missing something. ************************************************************* From: Robert I. Eachus Sent: Friday, May 16, 2003 9:51 AM Robert Dewar wrote: > I must say I can't see any significant problem with Robert (Eachus) proposal, > but perhaps I am missing something. I feel the same way. I think the proposal I put on the table solves the problems that caused the pragma approach to be dropped. And as far as I can see, the current synax change proposal doesn't. So either I am doing a bad job explaining it, or we are missing something... ************************************************************* From: Robert Dewar Sent: Friday, May 16, 2003 6:14 AM and they might as well ask their favorite vendor for a parser patch. But a "parser patch" is non-conforming, so either has to be under a special switch, or we have to wait till the entire WG9 approval process is complete. The pragma proposal can be implemented in a couple of hours, but of course it's not such a good idea to do this if the ARG is still twiddling its thumbs on the issue. I must say I would be more receptive to the syntax proposal if there was some decent syntax, and none of this OR NOT nonsense. ************************************************************* From: Robert Dewar Sent: Friday, May 16, 2003 6:56 AM > does not follow the syntax rules because 2.8(3) only has name or > expression, not access_definition. I realize that you can invent a > convention to handle this case, but it becomes quite distasteful. Plus, > you have to remember that we consider adding anonymous > access-to-subprogram types as part of the amendment, and these types > would be intractable using a pragma without new syntax. I don't see the need for new syntax to handle this case, it would seem to be for example that integer'access would be an acceptable way of designating such a type. Interestingly, in GNAT we have all the DEC pragmas, and they are quite useful, e.g. for specifying that a specific argument be passed by reference. (obviously "quite useful" here is talking about rather specialized interface situations :-) That means that GNAT has a bug that we cannot differentiate in the case of access parameters, but not too surprisingly, no one ever reported this bug :-) I guess we should implement the type'access syntax in the context of these pragmas. Interesting (obviously we are not about to add new syntax). But indeed following this path for the overriding is getting unacceptably kludgy, so the only reasonable pragma approach would be that suggested by Robert Eachus. I am confused at this stage on the alternative, what is the latest greatest suggestion for concrete syntax additions to the subprogram declarations. By the way, the implementation burden of any syntax additions is always significant, compared to pragmas, so I strongly disgaree with Randy there. For syntax additions, you have to make changes to coding standards documents, pretty printers, editors that understand syntax, etc. The compiler changes are of course trivial, but that's never where the main work is for a change to the syntax. ************************************************************* From: Robert Dewar Sent: Friday, May 16, 2003 7:18 AM > Although it is sometimes fruitful to re-open certain decisions if > there are some new insights, I haven't heard anything sufficiently > new here to justify re-opening this one. We really have gone > through this decision process quite carefully, and we reached a > consensus. I realize that if someone didn't attend an ARG meeting > they couldn't contribute or not contribute to such a consensus, Consensus in such an issue can be deceiving. After all the DR's had a very clear consensus that the syntax for what we now call tagged types should include the word class. We voted on that and it was a pretty close to unanimous vote. But in practice no one came up with a syntax that people found generally acceptable. More accurately, the prefix proposal narrowly failed because people did not like the syntax (indeed it should really have passed, because the Japanese delegation did not understand that by voting against it they would lose CLASS all together, which they really preferred, if Japan had voted positively, it would have passed 4-2). So what happened, is that from failure to find nice syntax for CLASS, we ended up with the peculiar keyword TAGGED, when there was a clear consensus against it. In this case, there is a consensus to use syntax. Sounds reasonable, I think I would have certainly signed up to this general notion. But then you have to come up with actual syntax, and if that actual syntax includes OR NOT, all I can say is that I find this as ugly as Tucker found the prefix CLASS syntax :-) ************************************************************* From: Tucker Taft Sent: Friday, May 16, 2003 7:23 AM Robert Dewar wrote: > ... > I am confused at this stage on the alternative, what is the latest greatest > suggestion for concrete syntax additions to the subprogram declarations. For the basic case of overriding outside of a generic, we have pretty much settled on: overriding procedure Blah(...); This can be used in a private part, even if the subprogram is declared in the visible part, to indicate that the subprogram is overriding a privately-inherited operation. There is a configuration pragma (usable either library-wide or per source file) which specifies that the default is non-overriding. In the absence of the pragma, the default is might or might not override (i.e. the current rule). What remains under debate is how to handle the case in a generic where the operation doesn't override in the generic, but it might or might not override in the instance. John proposed "overriding or not" as a way of allowing overriding in the instance. I have countered with requiring the explicit use of "not overriding" in the (presumably rare) cases where you want to disallow overriding in the instance. In the absence of explicit "not overriding", the default would be no additional check in the instance. As part of this, I would allow an explicit "not overriding" anywhere, as a way to be explicit, and to avoid any reliance on a configuration pragma's presence. Obviously this debate is still underway, and input on *this* issue would be very timely ;-) ************************************************************* From: Pascal Leroy Sent: Friday, May 16, 2003 10:01 AM > type Widget is new ... with private; > > procedure Add(W: in out Widget;...); > procedure Add(W: in out Widget;...); > procedure Add(W: in out Widget;...); > pragma Overrides(Add); > > procedure Add(W: in out Widget;...); > procedure Add(W: in out Widget;...); > procedure Add(W: in out Widget;...); > pragma No_Overriding(Add); An approach like that was considered and rejected because we want people to be able to add overriding indicators to existing code relatively easily, and here they would have to reorder declarative parts which may be quite large. Not only is that a lot of error-prone work, but it creates lots of spurious differences in versions control systems. Of course you could use the pragmas to delimit very small regions, containing only one or two subprograms, but that starts to look like C++, only uglier. ************************************************************* From: Robert I. Eachus Sent: Friday, May 16, 2003 1:01 PM Pascal Leroy wrote: > An approach like that was considered and rejected because we want > people to be able to add overriding indicators to existing code > relatively easily, and here they would have to reorder declarative > parts which may be quite large. Not only is that a lot of > error-prone work, but it creates lots of spurious differences in > versions control systems. I would rather say that it MAY result in a large number of changes in some cases. But the syntax approach requires changing every one of the potentially overloading declarations. > Of course you could use the pragmas to delimit very small regions, > containing only one or two subprograms, but that starts to look like > C++, only uglier. I agree with the aesthetics issue. The question becomes when will the pragma approach get ugly. For my code, one or two pragmas per derived tagged type is much preferrable to changing every subprogram declaration. Others have different styles, as Randy explained, and I understand that. However, I still have yet to see how the current proposal solves the original problem. That is what really concerns me. Adding all the extra scaffolding of extra program declarations to me doesn't solve the original problem. For example: with Ada.Finalization; package Root is type Root_Type is tagged private; procedure Do_Something (Object : in out Root_Type; Data : in Natural); private type Root_Type is new Ada.Finalization.Controlled with null record; not overriding procedure Do_Something (Object : in out Root_Type; Data : in Natural); -- additional declaration required by new rules. overriding procedure Finalize (Object : in out Root_Type); -- 2 -- Finalization hidden in private part to since Controlled is -- hidden. end Root; with Root; package Leaf is type Derived_Type is new Root.Root_Type with null record; overriding procedure Do_Something (Object : in out Derived_Type; Data : in Boolean); -- 1 not overriding procedure Finalise (Object : in out Derived_Type); -- Note: Alternative spelling of "Finalize". -- and note that in the public view it is not overriding. end Leaf; with Leaf; procedure Sample is Obj : Leaf.Derived_Type; begin Leaf.Do_Something (Obj, 10); -- Calls (1). -- Finalization of Obj will call (2). end Sample; -- Click BOOM! If you prefer, make the procedure Finalise a generic instantiation so that no declaration is needed in the body. So now we not only have the same old problem but I have to go to all that trouble to sign my own death warrant. This is the problem I see with ANY solution that requires the overriding/non overriding declaration to located with the original declaration. Let's get a bit more heavy handed. Require that if a type is private, that if a type is derived from it there has to be a private part in the package and a re-declaration of any overriding subprogams there: with Root; package Leaf is type Derived_Type is new Root.Root_Type with null record; overriding procedure Do_Something (Object : in out Derived_Type; Data : in Boolean); not overriding procedure Finalise (Object : in out Derived_Type); -- Note: Alternative spelling of "Finalize". -- and note that in the public view it is not overriding. private overriding procedure Finalize (Object : in out Derived_Type); -- Whoops, no error message on the body because this time I spelled -- Finalize right. But at least even if the original declaration of -- Finalise is a generic instantiation, I'll get an error message -- when compiling the body, or when I don't provide one. end Leaf; With my approach you will always get an error message on the package specification. It may be a pretty confusing one if you have: with Root; package Leaf is type Derived_Type is new Root.Root_Type with null record; pragma Overriding(Derived_Type); procedure Do_Something (Object : in out Derived_Type; Data : in Boolean); procedure Finalise (Object : in out Derived_Type); -- Note: Alternative spelling of "Finalize". -- and note that in the public view it is not overriding. end Leaf; But you will always get one. If you use: pragma Overriding(Do_Something, Finalize); The error you will get will depend on how you spell the name in the pragma. But at least you will get something. The reason I am so sensitive about getting an error message on the specification is that on large development projects, the specification may be written and agreed to months before there is a body. ************************************************************* From: Tucker Taft Sent: Friday, May 16, 2003 2:05 PM Robert I. Eachus wrote: > ... > However, I still have yet to see how the current proposal solves the > original problem. That is what really concerns me. Adding all the > extra scaffolding of extra program declarations to me doesn't solve the > original problem. I'm afraid with all the to'ing and fro'ing, I have lost track of what is your "original problem." If it is reflected in what comes below, I don't see a problem. For example: > > with Ada.Finalization; > package Root is > type Root_Type is tagged private; > procedure Do_Something (Object : in out Root_Type; > Data : in Natural); > private > type Root_Type is new Ada.Finalization.Controlled with null record; > not overriding > procedure Do_Something (Object : in out Root_Type; > Data : in Natural); > -- additional declaration required by new rules. > overriding > procedure Finalize (Object : in out Root_Type); -- 2 > -- Finalization hidden in private part to since Controlled is > -- hidden. > end Root; > > with Root; > package Leaf is > type Derived_Type is new Root.Root_Type with null record; > overriding > procedure Do_Something (Object : in out Derived_Type; > Data : in Boolean); -- 1 > not overriding > procedure Finalise (Object : in out Derived_Type); > -- Note: Alternative spelling of "Finalize". > -- and note that in the public view it is not overriding. > end Leaf; > > with Leaf; > procedure Sample is > Obj : Leaf.Derived_Type; > begin > Leaf.Do_Something (Obj, 10); -- Calls (1). > -- Finalization of Obj will call (2). > end Sample; > > -- Click BOOM! I have lost you here. If a type is not *visibly* controlled somewhere within the immediate scope where the derived type is declared, there is no way you can override the Finalize operation, any way you spell it. See 7.3.1(6/1). > If you prefer, make the procedure Finalise a generic instantiation so > that no declaration is needed in the body. So now we not only have the > same old problem but I have to go to all that trouble to sign my own > death warrant. This is the problem I see with ANY solution that > requires the overriding/non overriding declaration to located with the > original declaration. I still don't understand the problem. We don't require the "overriding" annotation be on the original declaration. We allow an additional (re)declaration to be given in the private part if that is where the operation "becomes" an overriding. Below you have chosen to use "not overriding" explicitly, but that seems gratuitous, especially in this situation where you will be giving a contradictory (and wrong, given 7.3.1(6/1) annotation in the private part. If we do allow the explicit "not overriding" I might be tempted to disallow it in a case where it becomes overriding in the private part, somewhat analogous to the suggestion for leaving it out in a generic when it might become overriding in the instance. > Let's get a bit more heavy handed. Require that if a type is private, > that if a type is derived from it there has to be a private part in the > package and a re-declaration of any overriding subprogams there: > > > with Root; > package Leaf is > type Derived_Type is new Root.Root_Type with null record; > overriding > procedure Do_Something (Object : in out Derived_Type; > Data : in Boolean); > not overriding > procedure Finalise (Object : in out Derived_Type); > -- Note: Alternative spelling of "Finalize". > -- and note that in the public view it is not overriding. > private > overriding > procedure Finalize (Object : in out Derived_Type); > -- Whoops, no error message on the body because this time I spelled > -- Finalize right. But at least even if the original declaration of > -- Finalise is a generic instantiation, I'll get an error message > -- when compiling the body, or when I don't provide one. > end Leaf; This would get an error message because you can't override something you never have visibility on, per 7.3.1(6/1). That would break "privateness." Perhaps you need to reconstruct your example, but as of now, I don't see the problem. > > With my approach you will always get an error message on the package > specification. This is nice, I suppose, but this example at least doesn't illustrate your point, and the advantage of having the error on the spec is pretty small, in my view at least. (And I have worked on some pretty big Ada systems...) ************************************************************* From: Robert I. Eachus Sent: Friday, May 16, 2003 6:42 PM Tucker Taft wrote: > I have lost you here. If a type is not *visibly* controlled > somewhere within the immediate scope where the derived type is > declared, there is no way you can override the Finalize operation, > any way you spell it. See 7.3.1(6/1). Now my head really hurts! Are you saying that the original derived Finalize declaration really can't be overridden and that in this case (or in this case with Finalize spelled right) dispatching still calls the implicit operation? I hope not, and that this is just semantics. Let me create a better example (in current Ada): with Ada.Finalization; package Root is type Root_Type is tagged private; function Value (Object : Root_Type) return Integer; procedure Set (Object : in out Root_Type; Value: in Integer); private type Root_Type is new Ada.Finalization.Controlled with record Init_Case: Integer := 3; end record; procedure Initialize (Object : in out Root_Type); -- 1 -- Finalization hidden in private part to since Controlled is -- hidden. end Root; package body Root is function Value (Object : Root_Type) return Integer is begin return Object.Init_Case; end Value; procedure Set (Object : in out Root_Type; Value: in Integer) is begin Object.Init_Case := Value; end Set; procedure Initialize (Object : in out Root_Type) is begin Object.Init_Case := 1; end Initialize; end Root; with Root; package Leaf is type Derived_Type is new Root.Root_Type with null record; -- derived type is known to be tagged but not controlled. procedure Initialize (Object : in out Derived_Type); -- 2 -- Tucker says this is not overriding right? end Leaf; package body Leaf is procedure Initialize (Object : in out Derived_Type) is begin Set(Object, 2); end Initialize; end Leaf; with Leaf; with Ada.Text_IO; use Ada.Text_IO; procedure Sample is Obj : Leaf.Derived_Type; -- Which Initialize gets called? begin case Leaf.Value(Obj) is when 1 => Put_Line(" Root.Initialize Called."); when 2 => Put_Line(" Leaf.Initialize Called."); when 3 => Put_Line(" No initialization!"); when others => Put_Line(" I give up!"); end case; end Sample; E:\Ada\Test\AI-218>sample Leaf.Initialize Called. At least GNAT agrees with me. ;-) Seriously I think that Tucker may be correct, "by the book." But to not be able to declare Leaf.Initialize as overriding defeats the whole point. Sigh. So now even if we adopt my solution or the new syntax, we still have to change 7.3.1 to get the definition of overriding to agree with the commonsense definition. > I still don't understand the problem. We don't require > the "overriding" annotation be on the original declaration. > We allow an additional (re)declaration to be given in the > private part if that is where the operation "becomes" > an overriding. Below you have chosen to use "not overriding" > explicitly, but that seems gratuitous, especially in this > situation where you will be giving a contradictory (and wrong, > given 7.3.1(6/1) annotation in the private part. If we do > allow the explicit "not overriding" I might be tempted to > disallow it in a case where it becomes overriding in the > private part, somewhat analogous to the suggestion for > leaving it out in a generic when it might become overriding > in the instance. The first part certainly makes me happy. I don't have to lie, I am just allowed to. But we still have the problem. If I go to my example above and declare Leaf.Initialize, (or worse Leaf.Initialise) where can I declare it to be overriding? If my example makes your head hurt as much as mine does now, I apologize. But I can now see why Tucker and I have been sparring at cross purposes. In the example above, the compiler and author know that Leaf.Initialize is overriding (in the normal sense of the term) and that it will get called--by magic if you want to think of it that way--when an object of Leaf.Derived_Type is created. My understanding has always been that the intention of this AI was to make that agreement between author and compiler explicit. This will not only help insure that the author does in fact know what is going on, but will make it easier for readers to understand as well. But if ARM overriding does not have the commonly accepted meaning, then whether we go with a pragma or a syntax change to enforce that definition, in can only confuse the user. ************************************************************* From: Randy Brukardt Sent: Friday, May 16, 2003 11:06 PM Robert Eachus wrote: > Let me create a better example (in current Ada): ... > E:\Ada\Test\AI-218>sample > Leaf.Initialize Called. > > At least GNAT agrees with me. ;-) I tried this on Janus/Ada 3.1.2, ObjectAda 7.2, and GNAT 3.15a1. GNAT does as you say (which is clearly wrong), and Janus/Ada and ObjectAda clearly say "Root.Initialize Called" which is right. > Seriously I think that Tucker may be > correct, "by the book." But to not be able to declare Leaf.Initialize > as overriding defeats the whole point. Sigh. So now even if we adopt > my solution or the new syntax, we still have to change 7.3.1 to get the > definition of overriding to agree with the commonsense definition. You're advocating the complete destruction of privateness, which you cannot mean. Clients of Root.Root_Type (including all of Leaf) do not know that Root_Type is controlled. Allowing them to override Initialize (on operation that the type does not have unless you peek into the private part) is a serious breach of privacy. So it's the case that Initialize is never overridding in your example, and declaring it to be so would be an error (precisely the sort of bug that this extra syntax is intended to detect). That means GNAT has a bug here, but we don't (in general) want to base the language rules on compiler bugs. Now, if Leaf was a child of Root (Root.Leaf), then the situation is completely different, and perhaps that is the case that you meant. In that case, the overridding occurs in the private part, and you would need to put the overriding clause in the private part. ... > But we still have the problem. If I go to my example above > and declare Leaf.Initialize, (or worse Leaf.Initialise) where can I > declare it to be overriding? Nowhere, because it is never overridding. If you had Root.Leaf instead, you declare it to be overridding in the private part. No problem. There are even cases where you'd have to do that in the body. Ugly, but correct. > If my example makes your head hurt as much as mine does now, I > apologize. It only hurts because you seem willing to discard privateness, and I doubt that you meant to do that. > But I can now see why Tucker and I have been sparring at > cross purposes. In the example above, the compiler and author know that > Leaf.Initialize is overriding (in the normal sense of the term) and that > it will get called--by magic if you want to think of it that way--when > an object of Leaf.Derived_Type is created. And the compiler has a bug, and the author is wrong. We HOPE that an error is detected in this case! This is precisely the sort of problem that AI-218 was invented to solve, and I think your mistake here (and GNAT's) demonstrates nicely why this AI is important: the code may do silently do something other than is intended in a very subtle way. ************************************************************* From: Pascal Leroy Sent: Saturday, May 17, 2003 3:20 AM Randy reported: > I tried this on Janus/Ada 3.1.2, ObjectAda 7.2, and GNAT 3.15a1. GNAT does > as you say (which is clearly wrong), and Janus/Ada and ObjectAda clearly say > "Root.Initialize Called" > which is right. Apparently Randy is still unable to run Apex. For the record, Apex says "Root.Initialize Called" too, so there is consensus that the bug is in GNAT. I am surprised that this case is not checked by the ACATS, btw, as it seems like a pretty serious issue. > You're advocating the complete destruction of privateness, which you > cannot mean. Right, I nearly had a heart attack when I read Eachus' message. > I think your mistake here (and GNAT's) > demonstrates nicely why this AI is important: the code may do silently do > something other than is intended in a very subtle way. I find it frightening that there may be code out there that depends on the GNAT behavior, and will behave differently when ported to another compiler (or when the ACT folks fix GNAT). This is certainly a very strong justification for pursuing this AI. ************************************************************* From: Robert I. Eachus Sent: Saturday, May 17, 2003 11:22 AM Pascal Leroy wrote: > Apparently Randy is still unable to run Apex. For the record, Apex says > "Root.Initialize Called" too, so there is consensus that the bug is in > GNAT. I am surprised that this case is not checked by the ACATS, btw, > as it seems like a pretty serious issue. > Right, I nearly had a heart attack when I read Eachus' message. Well at least since I was NOT surprised by GNAT's behavior, and expected it you can see why we have been arguing at cross-purposes. > I find it frightening that there may be code out there that depends on > the GNAT behavior, and will behave differently when ported to another > compiler (or when the ACT folks fix GNAT). This is certainly a very > strong justification for pursuing this AI. No disagreement at all. But for the record what happens on the other compilers if: 1) Root.Initialize is declared publicly, although Root_Type is still private? 2) Leaf is a child of Root, but with a priavte part--which could possibly have nothing in it related to Derived_Type? 3) package Leaf is a child of Root, but still without a private part? 4) Same as above, but Initialize is declared by instantiating a generic subprogram from Root, so that Root.Leaf doesn't have a body either. 5) The body of Root has a with clause for Leaf, but Leaf is not a child of Root. I think my understanding now is: 1) Leaf.Initialize called, since it is overriding, although Root_Type is not visibly Controlled. 2) Leaf.Initialize is called. 3 & 4) Leaf.Initialize is also called. The AARM says: 7.3.1 (7.r) For all of these rules, implicit private parts and bodies are assumed as needed. 5) Root.Initialize is called. Anything else would be just too baroque. We definitely need this AI, and my reason for strongly favoring the pragma version now is that I need it with the GNAT bugfix. It goes without saying that I have programs that depend on the current GNAT behavior. I don't yet know if any will be broken by fixing the bug. (Other than this test case of course.) Most should still be correct because tbey are the child package case, but I'd have to look. And of course, for non-experts who may not even know what the AARM is or where to find it, getting AI-218 implemented soonest is extremely important. If language lawyers and compilers have trouble understanding the current rules (with AI-33 8652/0019 of course) non-experts will have no clue what to expect in these corner cases. ************************************************************* From: Pascal Leroy Sent: Monday, May 19, 2003 8:37 AM Robert E. asked: > 1) Root.Initialize is declared publicly, although Root_Type is still > private? Leaf.Initialize Called. Now that Leaf knows that Root_Type has a primitive Initialize, it can override it. > 2) Leaf is a child of Root, but with a private part--which could > possibly have nothing in it related to Derived_Type? > 3) package Leaf is a child of Root, but still without a private part? Not sure exactly what you mean here. If Leaf is a child of Root, Root.Initialize is declared in the private part, and Leaf.Initialize is declared in the visible part then: Leaf.Initialize Called. That's because, when you come to the private part of Leaf you discover that the parent type had a primitive Initialize, and overriding happens at this point. > 4) Same as above, but Initialize is declared by instantiating a generic > subprogram from Root, so that Root.Leaf doesn't have a body either. No bearing on the results, the fact that the primitive operation is declared by a normal subprogram or by an instantiation doesn't affect overriding. > 5) The body of Root has a with clause for Leaf, but Leaf is not a child > of Root. The with clause doesn't affect overriding, so assuming that Root.Initialize is declared in the private part: Root.Initialize Called. > It goes > without saying that I have programs that depend on the current GNAT > behavior. I don't yet know if any will be broken by fixing the bug. > (Other than this test case of course.) Most should still be correct > because tbey are the child package case, but I'd have to look. Yes, I guess that in most cases the derivation hierarchy will mimic the unit hierarchy, so the right thing will happen. Your example is pretty uncommon in this respect. *************************************************************