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?

Answer

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).


About the Author

Valentine Reboul joined AdaCore in 2012 after 3 years of experience in critical systems (Air Traffic Flow Management and Railway automation solutions). She now participates in the "Qualifying Machine" research project and is involved in training sessions given about Ada Language. She holds an engineering degree from the Ecole Nationale Supérieure d'Informatique et de Mathématiques Appliquées (Grenoble, France).