Version 1.4 of ais/ai-20218.txt

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

!standard 8.3(26)          03-06-06 AI95-00218-03/01
!class amendment 03-06-06
!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 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.
In order to insure that the overriding is in fact declared, we introduce an overriding_clause which consists of of a copy of the specification with an overriding prefix. This overriding clause has to have full conformance with the specification in the partial view and since such conformance is not defined for generic instances, the partial view may not be an instance. Typically, these clauses will appear in the private part prefixed by "overriding".
!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; overriding_clause ::= overriding_indicator subprogram_specification ;
Add to 8.3 after paragraph 23:
If an explicit declaration of a visible operation of a private type or a private extension by a subprogram_declaration, abstract_subprogram_declaration, or subprogram_renaming_declaration does not include an overriding_indicator then it is permitted to give an overriding_clause for the operation in the corresponding private part.
Such an overriding_clause shall occur following the corresponding full_type_declaration. Its profile shall be fully conformant with the profile of the subprogram_declaration, abstract_subprogram_declaration or subprogram_renaming_declaration.
Add to 8.3 after paragraph 26:
If an explicit declaration of an operation by a subprogram_declaration, abstract_subprogram_declaration, subprogram_renaming_declaration or generic_instantiation of a subprogram has the overriding_indicator "overriding" then it shall override another homograph at the point of the declaration. If an explicit declaration of an operation by a subprogram_declaration, abstract_subprogram_declaration, subprogram_renaming_declaration or generic_instantiation of a subprogram has the overriding_indicator "not overriding" then it shall not override another homograph at the point of the declaration. These rules do not apply in a generic_declaration. 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.
If an overriding_clause has the overriding indicator "overriding" then the corresponding operation shall override another homograph at the point of the declaration. If an overriding_clause has the overriding indicator "not overriding" then the corresponding operation shall not override another homograph at the point of the declaration. These rules do not apply in a generic_declaration. 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: Whether a homograph is implicitly declared after the declaration or overriding_clause (see 7.3.1 to see how this can happen) has no impact on these checks. Thus, the overriding_indicator accurately reflects the overriding state of the operation at the point of the declaration or clause.
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 not the subprogram specification. This is because we do not want them to be permitted in all syntactic situations which use subprogram specification such as generic subprogram declarations, formal subprogram parameters and subprogram body stubs. However, we do want them to apply to abstract subprograms, to renaming and to generic instantiations. For example
overriding procedure Op(X: T) is abstract;
overriding procedure Pop(Y: TT) renames Pip;
overriding procedure Pup is new Pap(P1 => A1, P2 => A2);
Observe that we cannot use an overriding indicator with a subprogram body given alone but must supply a distinct subprogram declaration if we need to include one. Recall that in Ada 95, a subprogram body given alone in a package body can override an inherited primitive operation although it cannot be a new primitive operation. It would be surprising to permit the overriding indicator with such a body given alone since the existence of the overriding would not then be clear just from the package specification. This applies to both tagged and untagged types.
Requiring the explicit declaration of overriding is used in some other programming languages, most notably Eiffel (which uses the keyword redefine). Other commonly used languages (including Java and C++) also have the same problem, but do not have the tradition of safety that Ada has.
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.
Subtle situations occur with generics and private types. We will consider generics first.
Overriding_indicators are not checked generic_declarations or instance bodies, but are checked in generic bodies and instance specification.
Consider
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;
Giving an overriding_indicator in this way is legal. The effect is be to force all actual types for GT to have an operation Op. This effectively modifies the contract of the generic to include that operation.
It has been suggested that this "implicit contract" is 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 generic above (Gen) has an implicit contract that GT has the same accessibility level as the instantiation of Gen. This contract is far less visible than the keywords "overriding" are.
In addition, it isn't possible to completely eliminate implicit contracts caused by overriding_indicators. If we enforced the rule in generic_declarations as well as instances, the declaration of Op would be illegal. This would eliminate the "implicit contract" for the use of "overriding". But the situation for "not overriding" is different. 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. So the implicit contract has reappeared.
We could eliminate this problem by not rechecking at all in instances. However, this means that overriding indicators may not actually reflect reality in instances. It would be very odd indeed if an instance of Gen2 above had an Op that was actually overriding, even though it is declared to be not overriding.
Note that if a user does not want an implicit contract (as in a mix-in, perhaps), they simply can omit any overriding_indicators.
Thus, given that Ada 95 already has implicit contracts, and these are clearly the most useful of the three options, we have adopted the rules as specified in the Wording section.
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. 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, then this implies a contract that the actual does have Op. Again, if this is not desired, omitting "overriding" returns the checking to the Ada 95 rules.
A somewhat similar situation arises with private types. Thus consider:
package P is type T is private; function "+" (Left, Right: T) return T; private type T is range 0 .. 100; -- "+" overrides end P;
as opposed to
package P is type T is private; function "+" (Left, Right: T) return T; private type T is new TT; -- "+" does not override end P;
The point is that the partial view does not reveal whether overriding occurs or not - nor should it since either implementation ought to be acceptable. We should therefore remain silent regarding overriding in the partial view. In order to permit checking when the full circumstances are known, we introduce the possibility of an overriding clause in the private part. This takes the form of an overriding indicator followed by a repetition of the subprogram specification whose profile has to be fully conformant with the profile given in the private view. An overriding clause is only necessary when the operation in the visible part does not have an overriding indicator and in the full view we want to give the true state of affairs. Thus
package P is type T is private; function "+" (Left, Right: T) return T; -- overriding not visible private type T is range 0 .. 100; overriding function "+" (Left, Right: T) return T; -- now we know end P;
Checking is only performed when an overriding indicator is given. Thus, in this example, no check is performed for the declaration, and one is performed for the overriding_clause.
Overriding_clauses are only allowed when the original declaration does not have an overriding_indicator. This restriction is not strictly necessary (there is no problem with checking overriding in multiple places), but it avoids lying to the user. The partial view should not explicit state something that is false; for instance:
package P is type T is private; not overriding function "+" (Left, Right: T) return T; -- overriding not visible private type T is range 0 .. 100; overriding function "+" (Left, Right: T) return T; -- Overriding_clause not allowed. end P;
Similarly, there is no value to repeating the overriding_indicator. It would be more likely to confuse the reader than to provide any value.
An overriding_clause is restricted to the private part of the unit that contains the declaration. This eases the requirements on checking that all primitive operations have appropriate overriding_indicators or clauses. (While we're not including such checks as part of this proposal, they are likely to be a common part of coding style requirements.) If overriding_clauses could be given other than in the private part, checking that all overridings are explicitly declared would require looking in bodies and nested units.
Full conformance is required between the profiles although it is not strictly necessary for identification purposes. However, permitting a lesser level might be confusing for the reader. Moreover, that the conformance rules required for overriding are different for tagged and untagged types (tagged require subtype conformance whereas untagged only require type conformance) so it seems a good idea to overkill with full conformance for both.
An overriding clause may not be given in the case where the operation is provided by a generic instantiation. This is because conformance rules are not defined for the profile of an instantiation. There are other restrictions on generic instantiations such as that they are not allowed to complete a subprogram declaration and so this restriction should not seem surprising. As a consequence, it is not possible to declare an instantiation as overriding if the instantiation is in the visible part, it is not overriding for the partial view, and the operation turns out to be an overriding one. This is probably not a frequent situation. The workaround is to give the instantiation a different name and then to rename it as the primitive operation:
package P is type T is private; procedure Pup is new Pap(P1 => A1, P2 => A2); procedure Op(Y: T) renames Pup;
private type T is new TT; -- TT has an Op and so it overrides overriding procedure Op(Y: T); -- the overriding clause on the renaming end P;
Similar examples occur with private extensions.
package P is type T is new T1 with private; procedure Foo; private type T is new T2 with ...; overriding procedure Foo; end P;
This covers the situation where T1 does not have the operation Foo but T2 (which of course is ultimately derived from T1) does have the operation.
If T1 does have the operation Foo, then an overriding indicator 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;
We check overriding based on the state at the point of the declaration. That is done so that the indicator always reflects the true state of the declaration. However, an operation that is not overriding at some point can later become overriding (because of the rules of 7.3.1(6)). This later overriding is ignored for the purposes of checking overriding.
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 package body Nested is -- P.C.Nested.Op overrides P.Op here (P.Op is inherited here). end Nested; end P.C;
In this situation, the types NT and NNT are not private types and so there is no possibility of an overriding clause. Still, the overriding_indicators are not legal on these declarations. We probably simply should omit the overriding_indicators to avoid breaking the abstraction (privateness) of the original type P.T.
Contrast this with
package P.C2 is type NT is new T with private; overriding procedure Op (Y : NT); -- Illegal, this doesn't override anything. package Nested is type NNT is new T with private; procedure Op (Y : NNT); -- OK. private type NNT is new T with null record; overriding procedure Op (Y : NT); -- Illegal, this still doesn't override anything. end Nested; private type NT is new T with null record; -- P.C.Op overrides P.Op here (P.Op is inherited here). overriding procedure Op (Y : NT); -- OK. end P.C2;
Here the types NT and NNT are private types and so overriding clauses are allowed. However, such a clause for Nested.Op would have to go in the body, which is not allowed. This seems OK, this sort of structure is likely be rare. Indeed, the error is likely to catch as many bugs as it causes, since it points out the visibility nightmare that the user has gotten themselves into.
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_for_Primitive_Operations);
with the semantics that any explicit declaration of a primitive operation must have an overriding_indicator or a overriding_clause.
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 enclosing declarative part, as a later overriding_clause may be seen. 2) It is possible to declare a type where the overridden declaration is not inherited until the body (one is given above). In such a case, this pragma would require using "not overriding" on the overriding declaration in the specification, which is an outright lie. (This case should be rare, however.) 3) Restrictions apply inside of generic units. However, many generic units will prefer to avoid implicit contracts by avoiding the use of overriding_indicators. 4) 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. 5) 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.
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 the cases that overriding_clauses are provided to handle. In this case, the pragmas must be separated from the declarations (presuming we are unwilling to violate privateness). 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 optional overriding as well. Placing the indicator at the front has the merit of being uniform and easily seen by the reader.
The keyword "overriding" seems natural but the 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 8.3(26)
To be done.
!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.

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




Questions? Ask the ACAA Technical Agent