Gem #137: Ada Quiz 2 - An Heir and a Spare?
by Valentine Reboul —AdaCore
Let's get started...
Following in the footsteps of the Ada quiz in Gem#86, here are ten short questions serving as a review of the Ada rules of inheritance. Try to answer them without using the compiler.
Q1 - Is there a compilation error?
Here are three packages:
package P1 is type T1 is range 1 .. 10; end P1; with P1; use P1; package P2 is type T2 is new T1; end P2; with P1; use P1; package P3 is procedure Proc (V : T1); end P3;
and a procedure Main:
with P1; use P1; with P2; use P2; with P3; use P3; procedure Main is V : T2; begin Proc (V); end Main; Is the call to Proc legal? Answer
Q2 - Is there a compilation error?
Here are two packages:
package P1 is type T1 is range 1 .. 10; procedure Proc (V : T1); end P1; with P1; use P1; package P2 is type T2 is new T1; end P2;
and a procedure Main
with P1; use P1; with P2; use P2; procedure Main is V : T2; begin Proc (V); end Main; Is the call to Proc legal? Answer
Q3 - What is the result of this call?
A package P is defined as follow:
package P is type T1 is range 1 .. 10; procedure Proc (V : T1); type T2 is range 1 .. 10; procedure Proc (V : T2); end P; with Ada.Text_IO; use Ada.Text_IO; package body P is procedure Proc (V : T1) is begin Put_Line ("1"); end Proc; procedure Proc (V : T2) is begin Put_Line ("2"); end Proc; end P;
and a procedure Main makes some calls:
with P; use P; procedure Main is V1 : T1; V2 : T2; begin Proc (V1); Proc (V2); Proc (T2 (V1)); Proc (T1 (V2)); end Main;
What is the output of the call to Main?
Q4 - Is there a compilation error?
Consider the following package:
package P is type T1 is range 1 .. 10; procedure Proc (V : T1); type T2 is new T1; type T3 is new T2; overriding procedure Proc (V : T3); end P; Is the declaration of Proc for type T3 legal? Answer
Q5 - Is there a compilation error?
Consider the following package:
package P is type T is tagged null record; V : T; procedure Proc (V : T); end P; Is the package legal? Answer
Q6 - Is there a compilation error?
Consider the following packages: package P1 is type T is tagged null record; V : T; end P1; with P1; use P1; package P2 is procedure Proc (V : T); end P2; Is the declaration of procedure Proc legal? Answer
Q7 - Is there a compilation error?
Considering the following package:
package P is type T1 is record F1 : Integer; end record type T2 is new T1 with record F2 : Integer; end record end P; Is the declaration of type T2 legal? Answer
Q8 - Is there a compilation error?
Considering the following package:
package P is type T is tagged null record; procedure Proc (V : T); end P;
and a procedure Main calling Proc:
with P; procedure Main is V : P.T; begin Proc (V); V.Proc; end Main; Are the calls legal? Answer
Q9 - Is there a compilation error?
Consider the following package:
package P is type T is tagged null record; procedure Proc (V : T); end P;
and a procedure Main making calls to Proc:
with P; use all type P.T; procedure Main is V : P.T; begin Proc (V); V.Proc; end Main; Are the calls legal? Answer
Q10 - Is there a compilation error?
Consider this package:
package P is type T1 is tagged null record; type T2 is null record; procedure Proc (V1 : T1; V2 : T2); end P;
and a procedure Main making calls to Proc:
with P; use all type P.T2; procedure Main is V1 : P.T1; V2 : P.T2; begin Proc (V1, V2); V1.Proc (V2); end Main; Are the calls legal? Answer
Answers:
A1 - Compilation Error
T1 and T2 are two different types (remember that "new" really does mean "new" :) ), and the procedure Proc only applies to Type T1.
However, a conversion of V to T1 would make the call legal:
procedure Main is V : T2; begin Proc (T1 (V)); end Main;
A2 - No Error
The procedure Proc is declared in the scope of T1, so it's a primitive operation of the type. And since T2 derives from T1, it inherits its primitives.
A3 -
The conversion to another type directs the calls to the other type's procedure.
The output is:
1 2 2 1
A4 - No Error
T2 implicitly inherits the primitive Proc from type T1.
It is perfectly fine to override this primitive for type T3.
Observe that:
- overriding and not overriding are optional reserved words.
- not overriding ensures that the subprogram is a new primitive operation of the derived type.
package P is type T1 is range 1 .. 10; procedure Proc (V : T1); type T2 is new T1; type T3 is new T2; overriding procedure Proc (V : T3); not overriding procedure Proc2 (V : T3); end P;
- An inherited operation can effectively be removed from a type by declaring an overriding abstract operation. However, note that in the case of a tagged type, the derived type itself must also be declared abstract.
package P is type T1 is range 1 .. 10; procedure Proc (V : T1); type T2 is new T1; overriding procedure Proc (V : T2) is abstract; end P;
A5 - Compilation Error
The primitive subprogram is illegal. This is a consequence of Ada's freezing rules. Once a variable of a tagged type is declared, it's illegal to declare further primitive operations of the type (the type is said to be frozen at that point). A tagged type can also be frozen when another type is derived from it. The freezing rules ensure that the representation of a type is fully determined by the compiler at the point where a type is frozen, and all of its primitive dispatching operations are known.
A6 - No Error
The procedure Proc is not declared within the scope of T. Therefore it is not a primitive operation of T.
So there is no issue of the type's freezing point here.
A7 - Compilation Error
A derivation from an untagged type cannot change the structure of the type. Here an attempt was made to derive T2 from T1 and extend the type. However, T1 is not a tagged type, so an error is reported at compilation time.
Here is a corrected version:
package P is type T1 is TAGGED record F1 : Integer; end record type T2 is new T1 with record F2 : Integer; end record end P;
A8 - Compilation Error
In the case of primitives of tagged types, when the first actual of a call is a controlling parameter (that is, the parameter is of the tagged type and can control dispatching), the call can be prefixed by the object. So, the call to V.Proc is legal. Note that this form of call was added to Ada 2005.
The compilation error comes from the fact the procedure Proc is not directly visible, and thus cannot be called in the form Proc (V) without being prefixed by the name of its package (P.Proc(V)), or by writing a "use clause" for package P, to make the operation visible.
A9 - No Error
It was said in A8 that a "use clause" for package P would make the procedure Proc visible. It would not be sufficient to give a "use type clause" for type P.T, because that would only make the operators of the type visible. However, in Ada 2012, a more general form of "use type" clause was added, where the addition of the reserved word all (that is, "use all type P.T") makes all of the type's primitive operations visible.
A10 - No Error
The procedure Proc is a primitive of type T2 as well as type T1. Therefore, the use clause "use all type P.T2" also makes Proc visible for the unprefixed call Proc (V1, V2).