!standard 8.3(26) 05-05-27 AI95-00218-03/09 !standard 2.9(2) !standard 6.1(2) !standard 6.1(3) !standard 6.1(30) !standard 6.3(2) !standard 8.5.4(2) !standard 10.1.3(3) !standard 12.3(2) !class amendment 03-06-06 !status Amendment 200Y 03-10-21 !status WG9 Approved 03-12-12 !status ARG Approved 10-0-0 03-10-03 !status work item 03-06-06 !status received 03-06-06 !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. !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 additional syntax with a new reserved word which indicates that overriding will or will not occur. The general idea is that subprogram declarations that are overriding are prefixed with "overriding", whereas subprogram declarations that are prefixed with "not overriding" must not override another routine. The same applies to subprogram bodies, renamings, and generic instantiations. If no prefix is given, the Ada 95 rules are used. This has the effect of turning off checking for that declaration. For instance, in a generic unit 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 can avoid the use of a prefix. In the case of a subprogram which is a visible operation of a private type (or private extension), whether it is overriding or not may depend on precisely where the check is made. It might be that the operation is not visibly overloaded but turns out to be overloaded when the full type declaration occurs. Therefore, we define the checks to be made at the place of the declaration or clause with the prefix. For the case in question, no prefix would be given on the visible declaration. The prefix could be given on the body, if desired. !wording In 2.9(2) add the following to the list of reserved words overriding Change the syntax in 6.1 to read: subprogram_declaration ::= [overriding_indicator] subprogram_specification; abstract_subprogram_declaration ::= [overriding_indicator] subprogram_specification is abstract; Change the first two lines of 6.3(2) to read: subprogram_body ::= [overriding_indicator] subprogram_specification is Add the following after 6.1(30): An overriding_indicator is used to indicate whether overriding is intended. See 8.3.1, "Overriding Indicators". Add a new subclause 8.3.1: 8.3.1 Overriding indicators An overriding_indicator is used to declare that an operation is intended to override (or not override) an inherited operation. Syntax overriding_indicator ::= [not] overriding Legality Rules If a subprogram_declaration, abstract_subprogram_declaration, subprogram_body, subprogram_body_stub, subprogram_renaming_declaration, or generic_instantiation of a subprogram has an overriding_indicator, then: * the operation shall be a primitive operation for some type; * if the overriding_indicator is "overriding", then the operation shall override a homograph at the place of the declaration or body; * if the overriding_indicator is "not overriding", then the operation shall not override any homograph (at any place). 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" and "not overriding" rules differ slightly. For "overriding", we want the indicator to reflect the overriding state at the place of the declaration; otherwise the indicator would be 'lying'. Whether a homograph is implicitly declared after the declaration (see 7.3.1 to see how this can happen) has no impact on this check. However, "not overriding" is different; 'lying' would happen if a homograph declared later actually is overriding. So, we require this check to take into account later overridings. That can be implemented either by 'peeking', or by rechecking when additional operations are declared. The 'no lying' rules are needed to prevent a subprogram_declaration and subprogram_body from having contradictory overriding_indicators. End AARM Notes. Change the syntax in 8.5.4 to read subprogram_renaming_declaration ::= [overriding_indicator] subprogram_specification renames callable_entity_name; Change the syntax in 10.1.3 to read subprogram_body_stub ::= [overriding_indicator] subprogram_specification is separate; 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 syntax. 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 forms "overriding" and "not overriding" are part of a subprogram_declaration (and other forms) 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 and formal subprogram parameters. However, we do want them to apply to abstract subprograms, to bodies, to stubs, to renamings 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); Overriding indicators are also allowed on subprogram bodies. 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. An overriding indicator in this case can show the reader that overriding has occurred (and the compiler can check this intent). 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. The additional checking provided by overriding_indicators do not quite detect all possible overriding errors. Overriding_indicators cannot detect a problem if there exist two or more overloaded inherited routines, and the user overrode the wrong one. However, this should be extremely rare, as it is normally the case that all of the inherited routines with a single name are overridden as a group. An important principle is that an overriding_indicator never 'lie' to the user. That is, it should never be the case that an operation is marked "overriding" when it is not, nor should it be marked "not overriding" when it is. This matters when private types are brought into the picture. Assume type T2 has an operation Foo, but its parent type T1 does not. Then package P is type T is new T1 with private; procedure Foo (Obj : in out T); -- Can't give "overriding" here. private type T is new T2 with ...; end P; We don't allow giving "overriding" on Foo, as it is not an overriding operation of the partial view. To a user looking at the package from outside without seeing the private part, there is no Foo to override from. So, allowing the code to say otherwise would be confusing. On the other hand, we don't allow giving "not overriding" on Foo, either. That would be a worse lie, as the routine eventually will become overriding when the full declaration is given. The indicator "overriding" can be given on the subprogram_body, if desired. If T1 does have the operation Foo, then an overriding indicator can (and should) be given for the partial view. Thus package P is type T is new T1 with private; overriding procedure Foo (Obj : in out T); -- Partial view is overriding private type T is new T2 with ...; end P; Later overriding can occur not just in the private part, but at various other locations as well (because of the rules of 7.3.1(6)). This later overriding is ignored when checking "overriding" (because otherwise the declaration would lie), but is taken into account when checking "not overriding" (again to prevent lying). An example of how this could happen: 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); -- Illegal, 'Op' is not overriding here. -- The primitive 'Op' does not become visible -- until the private part. package Nested is type NNT is new T with null record; overriding procedure Op (Y : NNT); -- Illegal, 'Op' is not overriding here. -- Only the body of Nested has -- visibility on primitive 'Op'. end Nested; private -- P.C.Op overrides P.Op here (P.Op is inherited here). end P.C; package body P.C is overriding procedure Op (Y : NT) is ... -- OK. package body Nested is -- P.C.Nested.Op overrides P.Op here (P.Op is inherited here). overriding procedure Op (Y : NNT) is ... -- OK end Nested; end P.C; In this situation, the overriding_indicators have to be given on the bodies. Note that in the case of Nested.Op, the overriding takes place in the body, so no earlier overriding_indicator could be supported. Examples like these are a second reason that overriding_indicators are allowed on bodies. Some drafts of this AI supported a distinct clause to allow giving the overriding_indicator at the point where the overriding actually occurred. This clause doesn't seem necessary. For tagged types, it would only be needed in cases (like the first one) where we wish to make the same operations visible in two branches of the tree, while obscuring the common ancestory. Typically, it would be better to hoist the operations to the common ancestor; in any case, this locution is likely to be rare. Thus, we provide a way to specify overriding for it (by putting it on the body), but we do not go out of our way to make it easy. There are also issues with generic units. Overriding_indicators are checked following the standard Ada 95 rule (12.3(11)). That is, they are checked based on the properties of the formals in the generic declaration and body, and are rechecked based on the properties of the actuals in the instance specification. Consider generic type GT is tagged private; package Gen is type DGT is new GT with private; overriding procedure Op (Z : DGT); -- Illegal, GT does not have an Op. private -- ... end Gen; Giving an overriding_indicator in this way is illegal. This avoids an "implicit contract" that GT have an Op. However, we still have an implicit contract when "not overriding" is used. Consider: generic type GT is tagged private; package Gen2 is type DGT is new GT with private; not overriding procedure Op (Z : DGT); -- OK, GT must not have an Op. private -- ... end Gen2; This unit is legal, as Gen2.DGT does not have an Op operation. However, since Legality Rules are rechecked in the instance, an instantiation of Gen2 would be illegal if GT in fact had an Op primitive subprogram. Because of this strange asymmetrical result, we also considered two other rules: 1) The standard 12.3(11) rule (with checking in the private part of the instance); 2) As (1), except that no checking is done in the generic_declaration; 3) As (1), except that no checking is done in the instance. (1) does not require rechecking of "overriding" in the instance (it cannot fail), while it does require checking of "not overriding" in both the declaration and instance. (2) and (3) does not require rechecking (each check is made only once). (3) eliminates "implicit contracts" completely, but overriding_indicators may very well be 'lying' about the situation in the instance. Thus, we rejected this possibility. That means that we are stuck with "implicit contracts" in some cases. It has been suggested that "implicit contract"s are confusing and hard to see. Perhaps, but many of the Legality Rules in Ada 95 have the effect of defining an implicit contract. For instance, even without "overriding", the generics above (Gen and Gen2) have an implicit contract that GT has the same accessibility level as the instantiation of Gen. This contract is far less visible than the reserved word "overriding" is. Note that if a user does not want an implicit contract (as in a mix-in, perhaps), they simply can omit any overriding_indicators. Deciding between (1) and (2) is hard. (2) allows "implicit contracts" for both "not overriding" and "overriding", and thus makes their behavior more symmetrical. If (2) was adopted, the original example is now legal: generic type GT is tagged private; package Gen is type DGT is new GT with private; overriding procedure Op (Z : DGT); -- OK, GT must have an Op. private -- ... end Gen; The effect would be to force all actual types for GT to have an operation Op. While this looks appealing from a consistency standpoint, it is questionable how useful it is. Therefore, we have adopted the standard Ada rule (which at least is consistent with the rest of the language). 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 an Op. private -- ... end Gen; If the type T has Op as an operation then there is not an added contract (all possible types will have Op). However, if T does not have Op as a primitive, the generic unit is illegal. Overriding_indicators are allowed on all types. We cannot restrict them to just tagged types, because doing so would lead to contract model problems. While the primary use for this feature is with tagged types, we do need to insure that untagged types do not cause problems. 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. This is similar to the private extension case discussed earlier. "Overriding" would be illegal on both examples, while "not overriding" would be allowed only on the second one (which would constrain the implementation). Again, it is permissible to put an overriding_indicator on the body of "+" to indicate whether or not it is an overriding routine. It is possible for a routine to be primitive for more than one type and overriding for one of the types. Such a routine is considered 'overriding'. For instance: package Foo is type Root is tagged private; type Counter is range 0 .. 10_000; not overriding procedure Add (Obj : in out Root; Cnt : in Counter); -- Primitive on both Root and Counter. type Child is new Root with private; overriding procedure Add (Obj : in out Child; Cnt : in Counter); -- Primitive on both Child and Counter, and overriding (for Child). private type Root is tagged record Cnt : Counter := 0; end record; type Child is new Root with null record; end Foo; The second "Add" is overriding: the primitive Add is inherited (as it is declared before the declaration of Child); it is overriding (because it has the same profile as the inherited routine); and it is primitive for two types (because it is declared in the same package specification as Counter, so it is primitive for both Child and Counter.) A routine is considered overriding if it is overriding for any type. This is the most natural interpretation, but it may cause surprises if a routine is inherited from some untagged type. This proposal only increases the safety of Ada programs if the programmer uses overriding_indicators (especially "overriding") frequently. We expect that style guides and coding rules will evolve to require the use of overriding_indicators. We considered having a standard pragma in order to require the use of indicators in declarations. One suggestion was a new Restriction: pragma Restrictions (Indicators_Required); with the semantics that any explicit declaration of a primitive operation must have an overriding_indicator (either on the specification or the body). However, while this seems appealing at first glance, there are a number of issues: 1) The check has to be delayed until the end of the compilation of the body, as an overriding_indicator may be seen on the body. 2) Restrictions apply inside of generic units. However, many generic units will prefer to avoid implicit contracts by avoiding the use of overriding_indicators. Moreover, there are examples where no indicator can be given (as shown above). 3) These rules apply to all primitive operations, including untagged ones. The value of overriding indicators is much less for untagged types, and giving them for all operators could be a burden. 4) Restrictions are generally partition-wide, and many compilers do not support their use on individual units. The adoption of overriding_indicators is likely to be piece-meal, and it would be valuable to use them in some subsystems while still including other subsystems that do not. While solutions can be found for all of these problems, it doesn't seem likely that a one-size-fits-all solution can be found (particularly for the generic issues). Moreover, the greatest value of this proposal is simply allowing the programmer to declare overriding routines (and getting an error if that intent is not followed). The vast majority of problems of this sort are cases where Finalize or another overridden routine is not called when the programmer fully expects it to be called. The compiler knows otherwise, but cannot tell the programmer of their mistake. We considered using further pragmas rather than syntax since syntax was originally considered to be too heavyweight a solution for this problem. 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 maintenance if the identifier is not included and including it seems a burden. In addition, a pragma solution does not cleanly handle specifying overriding for a body. Generally, similar pragmas (like Inline) are not allowed on bodies. Putting the pragma inside the body would be weird; putting it before it would violate the normal rules for pragmas. Putting the pragma after the body would separate it a long way from the specification that it applied to. We considered an alternative that allowed the pragmas to appear anywhere. This makes checking significantly more expensive. Moreover, because of overloading, some way of limiting the effect of the pragma is needed. Two options were considered: * Giving the full profile of the subprogram in the pragma. This can be done as was done in the DEC VAX pragmas (which is ugly, and does not handle access parameters cleanly), or by adding syntax to allow a profile in a pragma (which is more complex than the syntax change proposed). * Giving the pragmas a scope of application: pragma Not_Overriding (Add); procedure Add (O : in out T; I: Integer); pragma Overriding (Add); procedure Add (O : in out T; S: String); procedure Add (O : in out T; F: Float); However, this constrains the order of declarations, and makes it much more likely that the pragma would be missed. Consider what would happen if the Float version of Add were moved to the top without the pragma. Keep in mind that in real programs, these aren't one line subprogram declarations, but routines with 5 parameters and 20 lines of comments. A far-away pragma could get lost. An important goal of this amendment is that new features are cleanly integrated into the language; they shouldn't look "bolted-on". This feature will be used quite frequently in Ada programs; pragmas start out looking ugly and wordy and it seems unlikely that the passage of time would improve that. 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 not overriding as well. Placing the indicator at the front has the merit of being uniform and easily seen by the reader. The reserved word "overriding" seems natural but the use of "not overriding" is not as clear. The problem with "not overriding" is that it is not very different from "overriding", which has the opposite meaning. However, other obvious choices (like "overloading" or "overloaded") suffer from the same problem, and no programmer is likely to mistake the meaning of "not". !comment !corrigendum 2.9(02) !comment This is now done by AI-284-2. !comment !comment @dinsl !comment @b !corrigendum 6.1(02) @drepl @xcode<@fa> @dby @xcode<@fa> !corrigendum 6.1(03) @drepl @xcode<@fa@ft<@b>@fa<;>> @dby @xcode<@fa@ft<@b>@fa<;>> !corrigendum 6.3(02) @drepl @xcode<@fa@ft<@b>@fa< declarative_part >@ft<@b>@fa< handled_sequence_of_statements >@ft<@b>@fa< [designator];>> @dby @xcode<@fa@ft<@b>@fa< declarative_part >@ft<@b>@fa< handled_sequence_of_statements >@ft<@b>@fa< [designator];>> !corrigendum 6.1(30) @dinsa A subprogram declared by an @fa is abstract; a subprogram declared by a @fa is not. See 3.9.3, "Abstract Types and Subprograms". @dinst An @fa is used to indicate whether overriding is intended. See 8.3.1, "Overriding Indicators". !corrigendum 8.3.1(1) @dinsc An @fa is used to declare that an operation is intended to override (or not override) an inherited operation. @i<@s8<> @xcode<@fa@ft<@b>@fa<] >@ft<@b>> @i<@s8<> If a @fa, @fa, @fa, @fa, @fa, or @fa of a subprogram has an @fa, then: @xbullet @xbullet is @b, then the operation shall override a homograph at the place of the declaration or body;> @xbullet is @b, then the operation shall not override any homograph (at any place).> 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. !corrigendum 8.5.4(2) @drepl @xcode<@fa@ft<@b @i>@fa> @dby @xcode<@fa@ft<@b @i>@fa> !corrigendum 10.1.3(3) @drepl @xcode<@fa@ft<@b>@fa<;>> @dby @xcode<@fa@ft<@b>@fa<;>> !corrigendum 12.3(2) @drepl @xcode<@fa@ft<@b>@fa< defining_program_unit_name >@ft<@b @b @i>@fa@ft<@b>@fa< defining_program_unit_name >@ft<@b @b @i>@fa@ft<@b>@fa< defining_designator >@ft<@b @b @i>@fa> @dby @xcode<@fa@ft<@b>@fa< defining_program_unit_name >@ft<@b @b @i>@fa@ft<@b>@fa< defining_program_unit_name >@ft<@b @b @i>@fa@ft<@b>@fa< defining_designator >@ft<@b @b @i>@fa> !ACATS test Tests should be created to check on the implementation of this feature. !appendix From: Randy Brukardt [This is a highly abridged message which explains the idea behind this alternative. See alternative 2 for the full thread.] I'm wondering personally if the idea of catching "accidental 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). ************************************************************* From: Randy Brukardt Sent: Friday, June 6, 2003 10:09 PM [Editor's note: This is version /01 of the AI.] Attached is alternative 3 of AI-218. This alternative approach grew out of private discussions between John, Pascal, and I over John's drafts of alternative 2 of AI-218. It's clear that being able to write: overriding procedure Finalize (Obj : in out T); is the meat of the proposal. Simply having the compiler tell you when this does not override will eliminate a lot of bugs (and debugging sessions). Everything else in the proposal is the broccoli. (I know that the cliche is "gravy", but broccoli seems more appropriate, because if its on your plate you eat it, because you think its good for you. Whether it really is, or whether it's edible at all, isn't always clear.) Anyway, it possible to eliminate a lot of the broccoli with a few simple (but radical) changes: First, we get rid of "overriding or not" or "maybe overriding" or "optional overriding" or "ignore overriding" or whatever it is called today. Rather, we replace it by "not overriding", which is applied to routines that want to declare to be, umm, not overriding. Declarations with this modifier are flagged if in fact they are overriding. Second, we change the checking to occur at the point of the modifier, using the state of overriding at that point. This eliminates the leakage of privateness that Pascal was concerned about in our private conversations. The overriding check does not take into account any overriding that might be happening later (in the private part or body). Third, we revert the default to Ada 95 rules. This eliminates most of the reasons for treating generics differently than other units; don't care now becomes "say nothing", which seems most appropriate. If you do say something, you get appropriate checking. These changes eliminate any need for a configuration pragma, although it still might be a useful idea. With these changes, it is essentially a programmer style issue whether or not to use the indicators, and I'd expect coding rules (and reviews) and style checkers to enforce them. Pascal had suggested that the pragma be a Restriction, as that can look through generics and privateness, which does help some. But the problem with the pragma is how to handle generics. You pretty much have to use different rules in generics than in the rest of the program for this, but the rules to use depend a lot on the organization's coding style. Still, there might be benefit in standardizing such a pragma. The nice thing about this alternative is that the pragma is completely separate from the meat of the proposal, and many of the nasty problems are with the pragma and its checking. If we can't solve those problems, we can simply forget the pragma and move on. I wrote a description of some of these issues in the !discussion section, but for this alternative, I dropped the pragma altogether. I also did a through analysis of the implicit contract issue, and came to a rather surprising conclusion. I did retain John's restrictions on the use of the overriding_clause, and tried to justify them. But it might make more sense to drop most of them. (Note that John's !discussion seems to assume that there are no such restrictions, but his wording makes it clear that there are several important ones. At least two examples he gives are illegal by his wording.) Comments welcome. ************************************************************* From: Robert Dewar Sent: Friday, June 6, 2003 10:47 PM personally I would take Randy's broccoli argument further and eliminate all of it, and just keep the overriding keyword and that's that (I would still prefer a pragma to the overriding, but I guess that's a lost cause). I really don't see the need for these perfectly horrible "or not" etc constructs. ************************************************************* From: Randy Brukardt Sent: Friday, June 6, 2003 11:48 PM We started without them. The primary goal was to have subprogram declarations marked "overriding" (using the current syntax) if and only if they are overriding operations. That has two benefits: one is the compile-time checking for errors (in both directions), and the other is increased readability (next year, you'll know whether "Add_Key" is overriding without having to page through a bunch of specs looking for "Add_Key" declarations). The "only if" seems trivial; it's an error if the keyword is given and the operation isn't overriding. The "if" is messier; it can't be done compatibly with Ada 95. So we started with a configuration pragma which required anything not marked to be not overridding, which works well for the simple cases. But both parts start having problems with private types (because the overriding situation can be different for the partial and full views, and you don't want to mandate breaking privateness). And also with generics (because they often are a don't care situation, where you really don't want to check). By replacing the configuration pragma by "not overriding", we at least get rid of most of the generic problems and eliminate the privacy breaking. And then a Restriction is about the right model for insuring the "if and only if". But I suppose we could give up on the original goal instead. If we only add the keyword, then it cannot be used in all cases (because there would be no way to handle a variety of private type cases - a number of them are given in the discussion of the most recent AI), so we couldn't have a language-defined way to implement the "if". Of course, an organization could still have coding standards that would go most of the way. ************************************************************* From: Robert I. Eachus Sent: Saturday, June 7, 2003 2:08 AM Robert Dewar wrote: > personally I would take Randy's broccoli argument further and eliminate all of > it, and just keep the overriding keyword and that's that (I would still prefer > a pragma to the overriding, but I guess that's a lost cause). I really don't > see the need for these perfectly horrible "or not" etc constructs. I still agree with Robert Dewar that a pair of pragmas is much better than kludging up the language. But I do think that this proposal is a huge step forward. I'd just like to go a bit further and request that one example show use of not overriding in the public part and overriding in the private part of a package. Unless people understand that particular case, no amount of documentation whether non-reserved words or pragmas can help: type Foo is private with null record; not overriding procedure Finalize(Object: in out Foo); private type Foo is new Ada.Finalization.Controlled with null record; overriding procedure Finalize(Object: in out Foo); ************************************************************* From: Robert Dewar Sent: Saturday, June 7, 2003 8:59 AM I think you need to hang a big sign up in the ARG room saying KISS :-) To me, this is a perfect example of a simple useful idea being cluttered up with rubbish. I would go JUST with the overriding keyword and be done with it (well as you know I would use a pragma to ease transition, but that's another issue, and I can live with the keyword). Now if you think it is useful to have a mode in which you get warnings or errors if the keyword is not used, that's a typical style issue that should be addressed by the implementation. Certainly it would be reasonable to have a warning when the keyword is not used when the subprogram does override, but I am not sure that we need language gizmos for that. If we start putting in language gizmos for this sort of style issue, things will definitely get cluttered. ************************************************************* From: Robert I. Eachus Sent: Saturday, June 7, 2003 10:00 AM Robert Dewar wrote: > I would go JUST with the overriding keyword and be done with it (well as you > know I would use a pragma to ease transition, but that's another issue, and > I can live with the keyword). I don't want to seem like Robert Dewar and I are in a tag team match, but it is clear that on this AI we are trying to keep our eye on the same objectives. What are we trying to do here? We are trying to make it possible for programmers to "comment" their code in a way that the compiler will verify. This particular feature is neeeded, I might even agree to necessary, in two situations: 1. A programmer is not sure whether or not a particular subprogram is overriding. (And possibly where it is overriding, or non-overriding.) In these cases a programmer will use the pragma or non-reserved word as an assertion. If his understanding of the language, or the program, is wrong, the compiler will tell him so. 2. The programmer knows what the rules are, and that this is a particularly tricky case. He wants to document for maintenance purpose exactly what is going on--and probably that his code depends on it. When we look at these cases, some things stand out. First any subtle rules about where 'overriding' or the pragma can occur are counterproductive. The programmer doesn't care that some group of language experts thought that this case was too tricky, or that "everyone knows" that overloading can't occur in this case, so that the keyword shouldn't be allowed. From the programmer's point of view, the purpose of this feature is documentation, and anything that gets in the way of using it for that is counterproductive. This is why I get nervous about such wonderful "features" of the keyword approach as requiring a duplicate declaration just to hang the keyword on, or not allowing the keyword on subprogram bodies. If it is documentation, I want to use it on generic instantiations and even on renamings of entries, or for that matter on entries themselves. I especially want to be able to use it twice in the case that some subprogram is not overriding in the visible part, but is overriding in the private part, or in the body. In fact, from my point of view, subprograms that are overriding in the private part of a child package but not in the private part of a non-child package are one of the most important cases for documentation. It may look to the maintenance programmer that making this a separate unit, or instantiating this generic as a child unit changes nothing. But it may not work, and the programmer should be able to use this feature to warn maintaniners. A similar argument applied to a with clause on a body whose only effect is to make some subprograms overriding. Also, requiring an extra declaration of a subprogram just so that the keyword can be put in the 'right' place is to me a major disadvantage of the keyword approach. It requires programmers to learn a new language feature just to cover an odd corner case. The problem is that, in most cases, the result will be that the keyword is not used, because the programmer doesn't know how to use the special language feature. The corner case for the pragma approach is one that I find much less troubling. When someone doesn't know how to use the pragma placement rules to get the intended effect runs into a problem, what will he do? Probably use a different name for the non-overriding homograph. I just don't see that as a problem. ************************************************************* From: Robert Dewar Sent: Saturday, June 7, 2003 10:29 AM Actually I have a bit of a different view from Robert Eachus. I don't see the point of allowing programmers who know what is going on to document things in a compiler checked way. Or more accurately, that is great to document all sorts of things, but why pick out this one. The use of the keyword to me seems to be the following. I write an overriding declaration. I want to make sure it overrides, so I use the keyword. That way I get warned if I have misspelled the spec, or if I misunderstand the rules. Going much beyond this seems overkill to me. I must say that we have very seldom seen customers get into trouble on this issue, and in the few cases that has happened, the very simple approach would have been more than adequate. I think we are gearing up the big cannons to fire at an enemy who barely exists here. ************************************************************* From: Tucker Taft Sent: Saturday, June 7, 2003 3:49 PM Thanks for writing this up, Randy. I like just having "overriding" and "not overriding." The "maybe overriding" was just too weird. I agree with the idea of putting the configuration pragma in a separate AI, making it clear that it is added value (or subtracted value ;-), but not essential to the basic proposal. The one thing that makes me uncomfortable is the rule where you say it is ignored in a generic declaration. That seems strange to me, and not justified in the examples I can construct. By and large, Ada doesn't care what kinds of overloading or overriding occurs in generic instances, but generic declarations are handled like non-generic declarations. You seem to be inverting this, where the *only* checking is in the instance. *Requiring* overriding in the instance seems positively weird, and a whole new concept. Can you construct an example where this would actually be desirable? So I like your proposal in general, but I would push for either: 1) Overriding and not overriding apply in both the generic decl and the instance (my mild preference); or 2) Overriding and not overriding applies only in the generic decl. You have proposed they apply *only* in the instance, and I don't think this is consistent with any other rule in the language, and I haven't been able to construct realistic and important examples where this rule would be sufficiently preferable to justify this inconsistent, one-of-a-kind rule. I don't see the "not overriding" as part of the "broccoli". It seems clear to me that if we provide an explicit "overriding" keyword, with the default being "either," then programmers will clearly preceive a hole in the language if we don't also provide "not overriding." ************************************************************* From: Randy Brukardt Sent: Tuesday, July 8, 2003 6:29 PM At the recent ARG meeting, we decided that there was no problem with overriding indicators for subprograms that are primitive for more than one type. The reason was that it was thought to be impossible to have such a routine that is overriding. I couldn't think of an example at the meeting, but I was dubious of that conclusion, as we had problems with such routines in Claw. I'm now fairly convinced that it is in fact possible to have such a routine that is overriding on one of the two types. Consider: package Foo is type Root is tagged private; type Counter is range 0 .. 10_000; not overriding procedure Add (Obj : in out Root; Cnt : in Counter); -- Primitive on both Root and Counter. type Child is new Root with private; overriding --??? procedure Add (Obj : in out Child; Cnt : in Counter); -- Primitive on both Child and Counter, and overriding (for Child). private type Root is tagged record Cnt : Counter := 0; end record; type Child is new Root with null record; end Foo; The second "Add" is overriding: the primitive Add is inherited (as it is declared before the declaration of Child); it is overriding (because it has the same profile as the inherited routine); and it is primitive for two types (because it is declared in the same package specification as Counter, so it is primitive for both Child and Counter.) (Aside: we did something like this in the root package of Claw, where the elementary type was "DWord". We didn't really want these routines to be primitive, but they are, and that meant they got inherited in some cases.) The overriding_indicator on the second Add is a problem. If you are thinking about the tagged types, you probably would expect it to be "overriding". But if you are thinking about "Counter", you would not expect it to be overriding. This case can't happen where both types are tagged types (Add would be illegal by 3.9.2(12) if Counter was tagged), but it certainly can happen in a case like this. My gut feeling is that we should simply define this to be overriding and not worry about it further. Comments? ************************************************************* From: Gary Dismukes Sent: Tuesday, July 8, 2003 6:46 PM I have the same feeling, that it should be treated as overriding. After all, the main purpose of this feature is to use it for tagged primitives, so it doesn't seem reasonable for the untagged parameter to interfere with that use. ************************************************************* From: Robert I. Eachus Sent: Friday, July 18, 2003 3:50 PM I feel like I am poking at a blister to see if it is still sore, but I think I have something (useful) to add that is new. The real problem, as I see it is that users are going to be seriously confused if the compiler tells them that a declaration is non-overriding when he knows that it WILL be.overriding. Changing the keyword overriding to mean will override it seems to me leads to other problems, but if we give users four choices, overriding, non-overriding, may override, and will override, we can limt the scope of the will override to mean will override in the (potentially implicit) private part. Or call it overrrides privately, or whatever. This post is not about the details. But if we have that, then the need to say may override, and create a new declaration in the private part to get the overriding status right. Further, we can say it is a bug to declare something non-overriding if it overrides in the private part. That to me has always been the most painful part of this. That a "true" declaration could be a lie as far as users were concerned. (And as far as I am concerned too. Overriding, as far as I am concerned is actually an execution time issue, and once we get there, something is either overriding or it isn't. Are there other cases where the overriding will only occur in the body that we need to deal with? (The only one I saw offhand was the implicit body case.) ************************************************************* From: Randy Brukardt Sent: Wednesday, September 17, 2003 10:22 PM The new version of AI-218-03 [version /02] that I just posted for comment contains six major changes: -- overriding_indicators are allowed on bodies; (minor open issue: should these be allowed on stubs? They are allowed on the actual subunit, so allowing them on stubs is redundant, but more consistent.) -- wording is added so that overriding_indicators must never 'lie'; that means that a 'not overriding' subprogram must never become overriding; -- wording was adjusted so that a routine primitive for more than one type can be considered 'overriding' (actually, this didn't require a change, but we now explicitly intend this result); -- wording is added so that overriding_indicators are only allowed on primitive operations; -- generics were changed to use the 'standard' mechanism, as discussed at the last meeting; -- overriding_clauses were removed. The discussion also has been completely overhauled. The removal of overriding_clauses needs a bit of explanation that I didn't want to crowd into the !disucssion. At the meeting, we wondered if we still needed overriding_clauses, given that it is now possible to give overriding_indicators on bodies. The feeling was that they were not necessary, but there was concern that they may be necessary in generic units. Clearly, by depending on overriding_indicators on bodies, we have a simpler change. It is true that the indicator may turn out to be given very late, but this cannot be worse than the current situation. And in virtually all tagged type cases (the primary use for the feature), the overriding_indicator can be given on the subprogram's specification. So, there doesn't seem to be enough need for the complication of overriding_clauses. Let's look at this in more detail. First, 'not overriding' can *always* be given on the subprogram specification. By the 'no lying' rule, you are not allowed to give it if in fact the routine is overriding at any point in the future. Thus, if you can use 'not overriding' at all, you can give it on the specification (it cannot change). That is true both inside and outside of generics. Second, the no overriding_indicator case is unchanged from Ada 95. No more needs to be said about that. So, all of the interesting cases involve the 'overriding' indicator. Let's look at them. First of all, virtually all tagged type cases can put the 'overriding' indicator on the specification. That is easily done by putting the overriding into the private part. Some style guides suggest that as a matter of course (because the user has no need to know whether or not a particular operation is overridden). But it almost always is an available option for tagged types. For instance, the following example is drawn from Claw: package Claw is type Root_Window_Type is abstract tagged private; procedure Move (Window : in out Root_Window_Type; Where : in Point_Type) is abstract; end Claw; package Claw.Basic_Window is type Basic_Window_Type is new Claw.Root_Window_Type with private; not overriding function Is_Valid (Window : Basic_Window_Type) return Boolean; private type Basic_Window_Type is new Claw.Root_Window_Type with ...; overriding procedure Move (Window : in out Root_Window_Type; Where : in Point_Type); end Claw.Basic_Window; It is OK to put the overriding into the private part. Note that the overriding clause here will catch the cut-and-paste error I made here (unintentially, I might add, one that has happened to me a number of times in real code), as the routine isn't primitive. In the case that the operation is not visible in the partial view, but is desired to be made visible, we cannot use this paradigm. For instance: package Claw.Basic_Window.Menu is type Menu_Window_Type is new Claw.Root_Window_Type with private; overriding function Is_Valid (Window : Basic_Window_Type) return Boolean; -- Illegal, not overriding here, will become overriding in the private part. private type Menu_Window_Type is new Claw.Basic_Window.Basic_Window_Type with ...; overriding procedure Move (Window : in out Menu_Window_Type; Where : in Point_Type); end Claw.Basic_Window.Menu; In this case, the overriding_indicator has to be placed on the body of Is_Valid. Note, also that 'not overriding' is not allowed on Is_Valid, either, as it will become overriding later. This doesn't seem too bad -- making 'hidden' operations visible this way should be rare (usually the operations are hidden for a reason!). Untagged types are pretty similar (although more cases fall into the second category). But since this feature was designed mainly for tagged types, I find it hard to get excited about that. Turning to generic units. If no formals are involved, the situation is the same as for any other package (as described above). So we only need consider formals. The rules state that the legality rules are checked based on the properties of the formal. That means that 'overriding' is only legal in cases where the formal being derived from has the overriding routine. For tagged types, that means that the formal has to be a formal derived type. generic type Some_Window_Type is new Claw.Root_Window_Type with private; package Claw.Mixin_Generic is type Mixin_Window_Type is new Some_Window_Type with ...; overriding procedure Move (Window : in out Mixin_Window_Type; Where : in Point_Type); end Claw.Mixin_Generic; If a type is directly derived (as Mixin_Window_Type is above), 'overriding' can always be given on the routine specification. (The instance cannot change this legality.) In order to not be able to give this in the spec, we again have to turn to a private extension: generic type Some_Window_Type is new Claw.Root_Window_Type with private; package Claw.Another_Mixin_Generic is type Another_Mixin_Window_Type is new Claw.Root_Window_Type with private; overriding function Is_Valid (Window : Another_Mixin_Window_Type) return Boolean; -- Illegal, not overriding here. overriding procedure Move (Window : in out Another_Mixin_Window_Type; Where : in Point_Type); -- OK private type Another_Mixin_Window_Type is new Some_Window_Type with ...; end Claw.Another_Mixin_Generic; We cannot give the overriding_indicator on Is_Valid, as that function is not primitive for the formal type. Indeed, we cannot give it on the body, either, for the same reason. We cannot have a 'hidden contract' requiring an overriding routine. So we must omit the overriding_specification. Note that we can give 'not overriding' on Is_Valid's specification, and this does have the effect of a hidden contract. That is, we cannot instantiate Another_Mixin_Generic with Basic_Window_Type - the recheck of the instance will fail. (We could put 'not overriding' on the body, where it essentially will be ignored. Perhaps we need to disallow the use of 'not overriding' in a body? It could only fail if it was given on a routine imported, like Move above, which is relatively unlikely.) So, its clear that a overriding_clause is even less useful for the generic unit than it is for a normal unit. (That is, the comments from the meeting are backwards: an overriding_clause is only useful for non-generic types.) Thus, I deleted it completely, thus simplifying the presentation quite a bit. Comments welcome. ************************************************************* From: Bob Duff Sent: Sunday, June 12, 2005 9:49 AM I have three questions from my review of 7.6 through 9. Here's the first. AARM version 1.19. 8.3.1(3/2): 3/2 {AI95-00218-03} {AI95-00348-01} {AI95-00397-01} If ... has an overriding_indicator, then: ... 5/2 if the overriding_indicator is overriding, then the operation shall override a homograph at the point of the declaration or body; 6/2 if the overriding_indicator is not overriding, then the operation shall not override any homograph (at any point). I'm confused. If we have: package P is type T is tagged private; [not] overriding? procedure Finalize(X: in out T); private type T is new Limited_Controlled with...; end P; Am I allowed to say "overriding" on Finalize(T)? (I hope so.) But "declaration or body" could be taken to mean the construct that has the overriding_indicator. And the AARM says "For overriding, we want the indicator to reflect the overriding state at the point of the declaration", which would seem to imply that overriding is *not* allowed above. ************************************************************* From: Randy Brukardt Sent: Monday, June 13, 2005 9:15 PM > Am I allowed to say "overriding" on Finalize(T)? (I hope so.) No. There was a strong sentiment against indicators ever "lying" or leaking private information. And it's obvious (without looking into the private part) that there is no overriding going on there. However, you can put the indicator on the body, presuming you leave it off of the routine in the public part. (They can't conflict, and the rules are designed to insure that can't happen.) Moreover, in this case, you don't *really* want to make Finalize visible; and of course there is no problem doing the overriding in the private part, in which case you could give the indicator there. > But "declaration or body" could be taken to mean the construct that has the > overriding_indicator. And the AARM says "For overriding, we want the indicator > to reflect the overriding state at the point of the declaration", which would > seem to imply that overriding is *not* allowed above. Not only does it imply it, it's quite intended. This stuff took a very long time to understand and come to a consensus. Perhaps the consensus would have been different had you attended more meetings. :-) ************************************************************* From: Pascal Leroy Sent: Tuesday, June 14, 2005 1:27 AM > Am I allowed to say "overriding" on Finalize(T)? (I hope so.) No, you can't say "overriding" here. Finalize is not overriding at the point of the specification. Allowing the indicator would leak privacy. But of course you can (and should) put the indicator on the body of Finalize, because at the location of the body surely Finalize is an overriding. I think it all makes sense. In the situation that you describe, indicators should be on the body, because they are mostly addressing an implementation issue: you want to make it really sure that you didn't mistype the subprogram name. This should not concern the clients who are just assuming that your abstraction is well-behaved. The situation would be different if T were visibly derived from Limited_Controlled. In this case, it would make sense to advertise the fact that you override Finalize, and that this is indeed intentional. > But "declaration or body" could be taken to mean the > construct that has the overriding_indicator. And the AARM > says "For overriding, we want the indicator to reflect the > overriding state at the point of the declaration", which > would seem to imply that overriding is *not* allowed above. Yes, that's exactly the intent. *************************************************************