Version 1.2 of ais/ai-00218.txt

Unformatted version of ais/ai-00218.txt version 1.2
Other versions for file ais/ai-00218.txt

!standard 8.3(9-13)          99-03-29 AI95-00218/02
!standard 6.1(1)
!class amendment 99-03-23
!status work item 99-03-23
!status received 99-03-23
!priority Medium
!difficulty Medium
!subject Accidental overloading when overriding
!summary
In order to reduce problems with overriding of primitive operations, two pragmas are 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); end Root;
with Root; package Leaf is type Derived_Type is new Root.Root_Type with null record; procedure Do_Something (Object : in out Root_Type; Data : in Boolean); -- (3) procedure Finalise (Object : in out Root_Type); -- (4) -- Note: British 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 in 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.
In all of these cases, the Ada compiler is being actively harmful to the programmer, violating the Ada design principles of least surprise.
!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 will be incompatible with existing Ada 95 code. Therefore, we have to introduce an optional solution which applies only when the programmer asks for it.
Thus, we introduce two new pragmas, Explicit_Overriding and Overrides. Explicit_Overriding is a configuration pragma which requires explicit marking of overriding subprograms. Pragma Overrides is used to mark subprograms that are overriding. Unmarked subprograms are new declarations, and may not override another routine.
!wording
Add to 8.3 after paragraph 1:
The form of a pragma Explicit_Overriding is as follows:
pragma Explicit_Overriding;
The form of a pragma Overrides is as follows:
pragma Overrides [(subprogram_name)];
Add to 8.3 after paragraph 26:
Pragma Explicit_Overriding is a configuration pragma.
Pragma Overrides applies to the preceding subprogram_declaration. Pragma Overrides shall immediately follow the subprogram_declaration to which it applies, separated only by space_characters, format_effectors, and comments. The optional subprogram_name of a pragma Overrides shall be the same as the name of the subprogram_declaration to which it applies.
A subprogram_declaration to which pragma Overrides applies shall override the implicit declaration of a primitive subprogram.
For a primitive subprogram inherited in a unit to which a pragma Explicit_Overriding applies, an overriding explicit declaration shall be a subprogram_declaration to which pragma Overrides applies.
!example
Here is our original example using the new pragmas.
pragma Explicit_Overriding;
with Root; package Leaf is type Derived_Type is new Root.Root_Type with null record; procedure Do_Something (Object : in out Root_Type; Data : in Boolean); pragma Overrides; -- Error: procedure Finalise (Object : in out Root_Type); pragma Overrides; -- Error: -- Note: British 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
We need to add two pragmas in order that we can have override checking even when no overriding subprograms are declared. This checking will prevent accidental overriding.
Requiring the explicit declaration of overriding is used in some other programming languages, most notably Eiffel. They also have noticed this problem.
This additional checking still could allow an error to occur. The problem could still occur if there existed two or more overloaded inherited routines, and the user overrode the wrong one. However, this would be extremely rare, as it is normally the case that all of the inherited routines with a single name are overridden as a group.
We considered adding syntax rather than defining pragma Overrides. Syntax was determined to be too heavyweight a solution for an amendment. One idea considered using existing keywords was: subprogram_specification is not new; These ideas were not considered satisfactory; it was felt that proper systax would need a new reserved word "overriding" or "redefined". That is much too heavy to introduce at this time. A pragma solution also has the advantage that compilers can implement it, and users can use it, even before most compilers support it. If a compiler has not yet implemented the pragmas, it will ignore them, and will accept any legal program (along with some that would be illegal if the pragmas were accepted).
Pragma Overrides was defined in such a way that it can be treated almost as a part of the subprogram declaration. This eases the implementation, as a compiler can determine what kind of declaration it is processing before it starts doing so. (Without this rule, an implementation must postpone error messages until it reaches the next declaration. Since the next declaration could be almost anything, this would provide a distributed overhead.) In addition, it is important that this pragma is not accidentially separated from the subprogram declaration.
!appendix

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

Questions? Ask the ACAA Technical Agent