Version 1.8 of ais/ai-20218.txt

Unformatted version of ais/ai-20218.txt version 1.8
Other versions for file ais/ai-20218.txt

!standard 8.3(26)          03-10-21 AI95-00218-03/03
!standard 2.9(3)
!standard 6.1(2)
!standard 6.1(3)
!standard 6.3(2)
!standard 8.5.4(2)
!standard 12.3(2)
!class amendment 03-06-06
!status Amendment 200Y 03-10-21
!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 nonreserved keyword 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 "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 point 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(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 ::= [not] overriding 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 to 8.3 after paragraph 26:
If a subprogram_declaration, abstract_subprogram_declaration, subprogram_body, 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 it shall override another homograph at the point of the declaration or body;
* if the overriding_indicator is "not overriding", then it shall not override another homograph (at any point).
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 point 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.
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 and 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, formal subprogram parameters and subprogram body stubs. However, we do want them to apply to abstract subprograms, to bodies, 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);
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 be marked 'not overriding' when it is.
This matters when private types are brought into the picture.
Presume 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; -- 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; -- 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 separate 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 asymetrical 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 keyword "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 symetrical. If (2) was adopted, the original example:
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;
This would be legal if (2) is adopted. 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 ileggal.
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 a 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) This 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 to 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. 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 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 ways 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 do 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 would would happen if the Float version of Add were moved to the top without the pragma. Keep in mind that in real programs that these aren't one line subprogram declarations, but routines with 5 parameters and 20 lines of comments. A far-away pragma could get lost. Moreover, we still need the ability to redeclare primitives, which is new syntax (well, at least a new use for existing syntax which will have quite an impact on syntax-directed compilers).
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 will 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 keyword "overriding" seems natural but the use of "not overriding" is not as clear. The problem with "not overriding" is that it is rather similar to 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".
!corrigendum 2.9(03)
Replace the paragraph:
NOTES
6 The reserved words appear in lower case boldface in this International Standard, except when used in the designator of an attribute (see 4.1.4). Lower case boldface is also used for a reserved word in a string_literal used as an operator_symbol. This is merely a convention — programs may be written in whatever typeface is desired and available.
by:
Overriding
is a nonreserved keyword.
NOTES
6 The reserved words appear in lower case boldface in this International Standard, except when used in the designator of an attribute (see 4.1.4). Lower case boldface is also used for a reserved word in a string_literal used as an operator_symbol. This is merely a convention — programs may be written in whatever typeface is desired and available.
!corrigendum 6.1(02)
Replace the paragraph:
subprogram_declaration ::= subprogram_specification ;
by:
overriding_indicator ::= [not] overriding subprogram_declaration ::= [overriding_indicator] subprogram_specification ;
!corrigendum 6.1(03)
Replace the paragraph:
abstract_subprogram_declaration ::= subprogram_specification is abstract;
by:
abstract_subprogram_declaration ::= [overriding_indicator] subprogram_specification is abstract;
!corrigendum 6.3(02)
Replace the paragraph:
subprogram_body ::= subprogram_specification is declarative_part begin handled_sequence_of_statements end [designator];
by:
subprogram_body ::= [overriding_indicator] subprogram_specification is declarative_part begin handled_sequence_of_statements end [designator];
!corrigendum 8.3(26)
Insert after the paragraph:
A non-overridable declaration is illegal if there is a homograph occurring immediately within the same declarative region that is visible at the place of the declaration, and is not hidden from all visibility by the non-overridable declaration. In addition, a type extension is illegal if somewhere within its immediate scope it has two visible components with the same name. Similarly, the context_clause for a subunit is illegal if it mentions (in a with_clause) some library unit, and there is a homograph of the library unit that is visible at the place of the corresponding stub, and the homograph and the mentioned library unit are both declared immediately within the same declarative region. These rules also apply to dispatching operations declared in the visible part of an instance of a generic unit. However, they do not apply to other overloadable declarations in an instance; such declarations may have type conformant profiles in the instance, so long as the corresponding declarations in the generic were not type conformant.
the new paragraphs:
If a subprogram_declaration, abstract_subprogram_declaration, subprogram_body, subprogram_renaming_declaration, or generic_instantiation of a subprogram has an overriding_indicator, then:
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)
Replace the paragraph:
subprogram_renaming_declaration ::= subprogram_specification renames callable_entity_name;
by:
subprogram_renaming_declaration ::= [overriding_indicator] subprogram_specification renames callable_entity_name;
!corrigendum 12.3(2)
Replace the paragraph:
generic_instantiation ::= package defining_program_unit_name is new generic_package_name [generic_actual_part]; | procedure defining_program_unit_name is new generic_procedure_name [generic_actual_part]; | function defining_designator is <new generic_function_name [generic_actual_part];
by:
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];
!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.

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




Questions? Ask the ACAA Technical Agent