Contents   Index   Previous   Next


3.9.3 Abstract Types and Subprograms

1
   [{abstract type} {abstract data type (ADT): See also abstract type} {ADT (abstract data type): See also abstract type} {concrete type: See nonabstract type} An abstract type is a tagged type intended for use as a parent type for type extensions, but which is not allowed to have objects of its own. {abstract subprogram} {concrete subprogram: See nonabstract subprogram} An abstract subprogram is a subprogram that has no body, but is intended to be overridden at some point when inherited. Because objects of an abstract type cannot be created, a dispatching call to an abstract subprogram always dispatches to some overriding body.]

Language Design Principles

1.a
An abstract subprogram has no body, so the rules in this clause are designed to ensure (at compile time) that the body will never be invoked. We do so primarily by disallowing the creation of values of the abstract type. Therefore, since type conversion and parameter passing don't change the tag, we know we will never get a class-wide value with a tag identifying an abstract type. This means that we only have to disallow nondispatching calls on abstract subprograms (dispatching calls will never reach them).

Legality Rules

2
   {abstract type} {type (abstract)} An abstract type is a specific type that has the reserved word abstract in its declaration. Only a tagged type is allowed to be declared abstract.
2.a
Ramification: Untagged types are never abstract, even though they can have primitive abstract subprograms. Such subprograms cannot be called, unless they also happen to be dispatching operations of some tagged type, and then only via a dispatching call.
2.b
Class-wide types are never abstract. If T is abstract, then it is illegal to declare a stand-alone object of type T, but it is OK to declare a stand-alone object of type T'Class; the latter will get a tag from its initial value, and this tag will necessarily be different from T'Tag.
3
   {abstract subprogram} {subprogram (abstract)} A subprogram declared by an abstract_subprogram_declaration (see 6.1) is an abstract subprogram. If it is a primitive subprogram of a tagged type, then the tagged type shall be abstract.
3.a
Ramification: Note that for a private type, this applies to both views. The following is illegal:
3.b
package P is
    type T is abstract tagged private;
    function Foo (X : T) return Boolean is abstract; -- Illegal!
private
    type T is tagged null record; -- Illegal!
    X : T;
    Y : Boolean := Foo (T'Class (X));
end P;
3.c
The full view of T is not abstract, but has an abstract operation Foo, which is illegal. The two lines marked "-- Illegal!" are illegal when taken together.
3.d
Reason: We considered disallowing untagged types from having abstract primitive subprograms. However, we rejected that plan, because it introduced some silly anomalies, and because such subprograms are harmless (if not terribly useful). For example:
3.e/1
package P is
   type Field_Size is range 0..100;
   type T is abstract tagged null record;
   procedure Print(X : in T; F : in Field_Size := 0) is abstractabstract;
  . . .
package Q is
   type My_Field_Size is new Field_Size;
   -- implicit declaration of Print(X : T; F : My_Field_Size := 0) is abstractis abstract;
end Q;
3.f
It seemed silly to make the derivative of My_Field_Size illegal, just because there was an implicitly declared abstract subprogram that was not primitive on some tagged type. Other rules could be formulated to solve this problem, but the current ones seem like the simplest.
4
   For a derived type, if the parent or ancestor type has an abstract primitive subprogram, or a primitive function with a controlling result, then:
5
5.a
Ramification: Note that it is possible to override a concrete subprogram with an abstract one.
6
6.a
Reason: A function that returns the parent type becomes abstract for an abstract type extension (if not overridden) because conversion from a parent type to a type extension is not defined, and function return semantics is defined in terms of conversion. (Note that parameters of mode in out or out do not have this problem, because the tag of the actual is not changed.)
6.b
Note that the overriding required above can be in the private part, which allows the following:
6.c
package Pack1 is
    type Ancestor is abstract ...;
    procedure Do_Something(X : in Ancestor) is abstract;
end Pack1;
6.d
with Pack1; use Pack1;
package Pack2 is
    type T1 is new Ancestor with record ...;
        -- A concrete type.
    procedure Do_Something(X : in T1); -- Have to override.
end Pack2;
6.e
with Pack1; use Pack1;
with Pack2; use Pack2;
package Pack3 is
    type T2 is new Ancestor with private;
        -- A concrete type.
private
    type T2 is new T1 with -- Parent different from ancestor.
      record ... end record;
    -- Here, we inherit Pack2.Do_Something.
end Pack3;
    
6.f/1
T2 inherits an abstract Do_Something, but T2 is not abstract, so Do_Something has to be overridden. However, it is OK to override it in the private part. In this case, we override it by inheriting a concrete version from a different type. Nondispatching calls to Pack3.Do_Something are allowed both inside and outside package Pack3.
7
   A call on an abstract subprogram shall be a dispatching call; [nondispatching calls to an abstract subprogram are not allowed.]
7.a
Ramification: If an abstract subprogram is not a dispatching operation of some tagged type, then it cannot be called at all.
8
   The type of an aggregate, or of an object created by an object_declaration or an allocator, or a generic formal object of mode in, shall not be abstract. The type of the target of an assignment operation (see 5.2) shall not be abstract. The type of a component shall not be abstract. If the result type of a function is abstract, then the function shall be abstract.
8.a
Reason: This ensures that values of an abstract type cannot be created, which ensures that a dispatching call to an abstract subprogram will not try to execute the nonexistent body.
8.b
Generic formal objects of mode in are like constants; therefore they should be forbidden for abstract types. Generic formal objects of mode in out are like renamings; therefore, abstract types are OK for them, though probably not terribly useful.
9
   If a partial view is not abstract, the corresponding full view shall not be abstract. If a generic formal type is abstract, then for each primitive subprogram of the formal that is not abstract, the corresponding primitive subprogram of the actual shall not be abstract.
9.a
Discussion: By contrast, we allow the actual type to be nonabstract even if the formal type is declared abstract. Hence, the most general formal tagged type possible is "type T(<>) is abstract tagged limited private;".
9.b
For an abstract private extension declared in the visible part of a package, it is only possible for the full type to be nonabstract if the private extension has no abstract dispatching operations.
10
    For an abstract type declared in a visible part, an abstract primitive subprogram shall not be declared in the private part, unless it is overriding an abstract subprogram implicitly declared in the visible part. For a tagged type declared in a visible part, a primitive function with a controlling result shall not be declared in the private part, unless it is overriding a function implicitly declared in the visible part.
10.a
Reason: The ``visible part'' could be that of a package or a generic package. This rule is needed because a non-abstract type extension declared outside the package would not know about any abstract primitive subprograms or primitive functions with controlling results declared in the private part, and wouldn't know that they need to be overridden with non-abstract subprograms. The rule applies to a tagged record type or record extension declared in a visible part, just as to a tagged private type or private extension. The rule applies to explicitly and implicitly declared abstract subprograms:
10.b
package Pack is
    type T is abstract new T1 with private;
private
    type T is abstract new T2 with record ... end record;
    ...
end Pack;
10.c
The above example would be illegal if T1 has a non-abstract primitive procedure P, but T2 overrides P with an abstract one; the private part should override P with a non-abstract version. On the other hand, if the P were abstract for both T1 and T2, the example would be legal as is.
11
    A generic actual subprogram shall not be an abstract subprogram. The prefix of an attribute_reference for the Access, Unchecked_Access, or Address attributes shall not denote an abstract subprogram.
11.a
Ramification: An abstract_subprogram_declaration is not syntactically a subprogram_declaration. Nonetheless, an abstract subprogram is a subprogram, and an abstract_subprogram_declaration is a declaration of a subprogram.
11.b
The part about generic actual subprograms includes those given by default.
NOTES
12
74  Abstractness is not inherited; to declare an abstract type, the reserved word abstract has to be used in the declaration of the type extension.
12.a
Ramification: A derived type can be abstract even if its parent is not. Similarly, an inherited concrete subprogram can be overridden with an abstract subprogram.
13
75  A class-wide type is never abstract. Even if a class is rooted at an abstract type, the class-wide type for the class is not abstract, and an object of the class-wide type can be created; the tag of such an object will identify some nonabstract type in the class.

Examples

14
    Example of an abstract type representing a set of natural numbers:
15
package Sets is
    subtype Element_Type is Natural;
    type Set is abstract tagged null record;
    function Empty return Set is abstract;
    function Union(Left, Right : Set) return Set is abstract;
    function Intersection(Left, Right : Set) return Set is abstract;
    function Unit_Set(Element : Element_Type) return Set is abstract;
    procedure Take(Element : out Element_Type;
                   From : in out Set) is abstract;
end Sets;
NOTES
16
76  Notes on the example: Given the above abstract type, one could then derive various (nonabstract) extensions of the type, representing alternative implementations of a set. One might use a bit vector, but impose an upper bound on the largest element representable, while another might use a hash table, trading off space for flexibility.
16.a
Discussion: One way to export a type from a package with some components visible and some components private is as follows:
16.b
package P is
    type Public_Part is abstract tagged
        record
            ...
        end record;
    type T is new Public_Part with private;
    ...
private
    type T is new Public_Part with
        record
            ...
        end record;
end P;
16.c
The fact that Public_Part is abstract tells clients they have to create objects of type T instead of Public_Part. Note that the public part has to come first; it would be illegal to declare a private type Private_Part, and then a record extension T of it, unless T were in the private part after the full declaration of Private_Part, but then clients of the package would not have visibility to T.

Contents   Index   Previous   Next   Legal