7.3.1 Private Operations
[For a type declared in the visible part of a package
or generic package, certain operations on the type do not become visible
until later in the package — either in the private part or the
body.
Such
private operations are available
only inside the declarative region of the package or generic package.]
Static Semantics
The predefined operators that exist for a given type
are determined by the classes to which the type belongs. For example,
an integer type has a predefined "+" operator. In most cases,
the predefined operators of a type are declared immediately after the
definition of the type; the exceptions are explained below. Inherited
subprograms are also implicitly declared immediately after the definition
of the type, except as stated below.
{
8652/0019}
{
AI95-00033-01}
{
AI05-0029-1}
For a composite type, the characteristics (see
7.3)
of the type are determined in part by the characteristics of its component
types. At the place where the composite type is declared, the only characteristics
of component types used are those characteristics visible at that place.
If later immediately within the declarative region in which the composite
type is declared additional characteristics become visible for a component
type, then any corresponding characteristics become visible for the composite
type. Any additional predefined operators are implicitly declared at
that place. If there is no such place, then additional predefined operators
are not declared at all, but they still exist.
Reason: {
AI05-0029-1}
We say that the predefined operators exist because they can emerge in
some unusual generic instantiations. See
12.5.
Discussion: {
AI05-0029-1}
The predefined operators for the underlying class of a type always exist,
even if there is no visibility on that underlying class. This rule is
simply about where (if ever) those operators are declared (and thus become
usable). The “additional predefined operators” defined by
this rule are any that are not declared at the point of the original
type declaration. For instance, a type derived from a private type whose
full type is type String always will have a ">" operator,
but where that operator is declared (and thus whether it is visible)
will depend on the visibility of the full type of the parent type.
{
8652/0019}
{
AI95-00033-01}
The corresponding rule applies to a type defined by a
derived_type_definition,
if there is a place immediately within the declarative region in which
the type is declared where additional characteristics of its parent type
become visible.
{
8652/0019}
{
AI95-00033-01}
[For example,
an array type whose component type is limited private becomes nonlimited
if the full view of the component type is nonlimited and visible at some
later place immediately within the declarative region in which the array
type is declared. In such a case, the predefined "=" operator
is implicitly declared at that place, and assignment is allowed after
that place.]
{
AI12-0140-1}
The characteristics and constraints of the designated subtype of an access
type follow a somewhat different rule. The view of the designated subtype
of (a view of) an access type at a given place is determined by the view
of the designated subtype that is visible at that place, rather than
the view at the place where the access type is declared.
Ramification: {
AI12-0140-1}
Whether or not the designated subtype is considered incomplete is determined
by rules in
3.10.1; this rule has no effect
on that property.
{
AI05-0115-1}
{
AI05-0269-1}
{
AI12-0140-1}
A type is a
descendant of the full view of
some ancestor of its parent type only if the current view it has of its
parent is a descendant of the full view of that ancestor. More generally,
at any given place, a type is descended from the same view of an ancestor
as that from which the current view of its parent is descended. This
view determines what characteristics are inherited from the ancestor[,
and, for example, whether the type is considered to be a descendant of
a record type, or a descendant only through record extensions of a more
distant ancestor].
{
AI05-0115-1}
{
AI12-0065-1}
{
AI12-0140-1}
{
AI12-0451-1}
[Furthermore, it is possible for there to be places where a derived type
is known to be derived indirectly from an ancestor type, but is not a
descendant of even a partial view of the ancestor type, because the parent
of this derived type is not visibly a descendant of the ancestor. In
this case, the derived type inherits no characteristics from that ancestor,
but nevertheless is within the derivation class of the ancestor for the
purposes of type conversion, the "covers" relationship, and
matching against a formal derived type. In this case the derived type
is effectively a
descendant of an incomplete view of the ancestor.]
Discussion:
Here is an example of this situation:
package P is
type T is private;
C : constant T;
private
type T is new Integer;
C : constant T := 42;
end P;
{
AI12-0065-1}
with P;
package Q
is
type T2
is new P.T; --
T2 is not a descendant of Integer
end Q;
{
AI12-0065-1}
with Q;
package P.Child
is
type T3
is new Q.T2;
private
--
Here T3 is known to be indirectly derived from Integer, but inherits
--
no characteristics from Integer, since T2 inherits no characteristics
--
from Integer.
--
However, we allow an explicit conversion of T3 to/from Integer.
--
Hence, T3 is effectively a descendant of an "incomplete" view of Integer.
Int : Integer := 52;
V : T3 := T3(P.C); --
Legal: conversion allowed
W : T3 := T3(Int); --
Legal: conversion allowed
X : T3 := T3(42); --
Error: T3 is not a numeric type
Y : T3 := X + 1; --
Error: no visible "+" operator
Z : T3 := T3(Integer(W) + 1); --
Legal: convert to Integer first
end P.Child;
{
8652/0019}
{
AI95-00033-01}
{
AI05-0029-1}
Inherited primitive subprograms follow a different rule. For a
derived_type_definition,
each inherited primitive subprogram is implicitly declared at the earliest
place, if any, immediately within the declarative region in which the
type_declaration
occurs, but after the
type_declaration,
where the corresponding declaration from the parent is visible. If there
is no such place, then the inherited subprogram is not declared at all,
but it still exists. [For a tagged type, it is possible to dispatch to
an inherited subprogram that is not declared at all.]
Discussion: The above rules matter only
when the component type (or parent type) is declared in the visible part
of a package, and the composite type (or derived type) is declared within
the declarative region of that package (possibly in a nested package
or a child package).
Consider:
package Parent is
type Root is tagged null record;
procedure Op1(X : Root);
type My_Int is range 1..10;
private
procedure Op2(X : Root);
type Another_Int is new My_Int;
procedure Int_Op(X : My_Int);
end Parent;
with Parent; use Parent;
package Unrelated is
type T2 is new Root with null record;
procedure Op2(X : T2);
end Unrelated;
package Parent.Child is
type T3 is new Root with null record;
-- Op1(T3) implicitly declared here.
package Nested is
type T4 is new Root with null record;
private
...
end Nested;
private
-- Op2(T3) implicitly declared here.
...
end Parent.Child;
with Unrelated; use Unrelated;
package body Parent.Child is
package body Nested is
-- Op2(T4) implicitly declared here.
end Nested;
type T5 is new T2 with null record;
end Parent.Child;
Another_Int does not inherit Int_Op, because
Int_Op does not “exist” at the place where Another_Int is
declared.
Type T2 inherits Op1 and Op2 from Root. However,
the inherited Op2 is never declared, because Parent.Op2 is never visible
immediately within the declarative region of T2. T2 explicitly declares
its own Op2, but this is unrelated to the inherited one — it does
not override the inherited one, and occupies a different slot in the
type descriptor.
T3 inherits both Op1 and Op2. Op1 is implicitly
declared immediately after the type declaration, whereas Op2 is declared
at the beginning of the private part. Note that if Child were a private
child of Parent, then Op1 and Op2 would both be implicitly declared immediately
after the type declaration.
T4 is similar to T3, except that the earliest
place immediately within the declarative region containing T4 where Root's
Op2 is visible is in the body of Nested.
If T3 or T4 were to declare a type-conformant
Op2, this would override the one inherited from Root. This is different
from the situation with T2.
T5 inherits Op1 and two Op2's from T2. Op1 is
implicitly declared immediately after the declaration of T5, as is the
Op2 that came from Unrelated.Op2. However, the Op2 that originally came
from Parent.Op2 is never implicitly declared for T5, since T2's version
of that Op2 is never visible (anywhere — it never got declared
either).
For all of these rules, implicit private parts
and bodies are assumed as needed.
It is possible
for characteristics of a type to be revealed in more than one place:
package P is
type Comp1 is private;
private
type Comp1 is new Boolean;
end P;
package P.Q is
package R is
type Comp2 is limited private;
type A is array(Integer range <>) of Comp2;
private
type Comp2 is new Comp1;
-- A becomes nonlimited here.
-- "="(A, A) return Boolean is implicitly declared here.
...
end R;
private
-- Now we find out what Comp1 really is, which reveals
-- more information about Comp2, but we're not within
-- the immediate scope of Comp2, so we don't do anything
-- about it yet.
end P.Q;
package body P.Q is
package body R is
-- Things like "xor"(A,A) return A are implicitly
-- declared here.
end R;
end P.Q;
{
8652/0019}
{
AI95-00033-01}
We say
immediately within the declarative region in order that
types do not gain operations within a nested scope. Consider:
package Outer is
package Inner is
type Inner_Type is private;
private
type Inner_Type is new Boolean;
end Inner;
type Outer_Type is array(Natural range <>) of Inner.Inner_Type;
end Outer;
package body Outer is
package body Inner is
-- At this point, we can see that Inner_Type is a Boolean type.
-- But we don't want Outer_Type to gain an "and" operator here.
end Inner;
end Outer;
[The Class attribute
is defined for tagged subtypes in
3.9. In addition,]
for every subtype S of an untagged private type whose full view is tagged,
the following attribute is defined:
S'Class
Denotes the class-wide subtype
corresponding to the full view of S. This attribute is allowed only from
the beginning of the private part in which the full view is declared,
until the declaration of the full view. [After the full view, the Class
attribute of the full view can be used.]
NOTE 1 Because a partial view and
a full view are two different views of one and the same type, outside
of the defining package the characteristics of the type are those defined
by the visible part. Within these outside program units the type is just
a private type or private extension, and any language rule that applies
only to another class of types does not apply. The fact that the full
declaration can implement a private type with a type of a particular
class (for example, as an array type) is relevant only within the declarative
region of the package itself including any child units.
The consequences of this actual implementation are,
however, valid everywhere. For example: any default initialization of
components takes place; the attribute Size provides the size of the full
view; finalization is still done for controlled components of the full
view; task dependence rules still apply to components that are task objects.
NOTE 2 {
AI95-00287-01}
Partial views provide initialization, membership tests, selected components
for the selection of discriminants and inherited components, qualification,
and explicit conversion. Nonlimited partial views also allow use of
assignment_statements.
NOTE 3 For a subtype S of a partial
view, S'Size is defined (see
13.3). For an
object A of a partial view, the attributes A'Size and A'Address are defined
(see
13.3). The Position, First_Bit, and Last_Bit
attributes are also defined for discriminants and inherited components.
Examples
Example of a type
with private operations:
package Key_Manager
is
type Key
is private;
Null_Key :
constant Key; --
a deferred constant declaration (see 7.4)
procedure Get_Key(K :
out Key);
function "<" (X, Y : Key)
return Boolean;
private
type Key
is new Natural;
Null_Key :
constant Key := Key'First;
end Key_Manager;
package body Key_Manager is
Last_Key : Key := Null_Key;
procedure Get_Key(K : out Key) is
begin
Last_Key := Last_Key + 1;
K := Last_Key;
end Get_Key;
function "<" (X, Y : Key) return Boolean is
begin
return Natural(X) < Natural(Y);
end "<";
end Key_Manager;
{
AI12-0452-1}
Outside of the package Key_Manager, the operations available for objects
of type Key include assignment, the comparison for equality or inequality,
the procedure Get_Key and the operator "<"; they do not
include other relational operators such as ">=", or arithmetic
operators.
{
AI12-0440-1}
{
AI12-0452-1}
The explicitly declared operator "<" hides the predefined
operator "<" implicitly declared by the
full_type_declaration.
Within the body of the function, an explicit conversion of X and Y to
the subtype Natural is necessary to invoke the "<" operator
of the parent type. Alternatively, the result of the function can be
written as
not (X >= Y), since the operator ">="
is not redefined.
{
AI12-0452-1}
The value of the variable Last_Key, declared in the package body, remains
unchanged between calls of the procedure Get_Key. (See also the NOTEs
of
7.2.)
Wording Changes from Ada 83
The phrase in RM83-7.4.2(7), “...after
the full type declaration”, doesn't work in the presence of child
units, so we define that rule in terms of visibility.
The definition of the Constrained attribute
for private types has been moved to “Obsolescent Features”.
(The Constrained attribute of an object has not been moved there.)
Wording Changes from Ada 95
Wording Changes from Ada 2005
{
AI05-0029-1}
Correction: Revised the wording to say that predefined operations
still exist even if they are never declared, because it is possible to
reference them in a generic unit.
{
AI05-0115-1}
Correction: Clarified that the characteristics of a descendant
of a private type depend on the visibility of the full view of the direct
ancestor. This has to be the case (so that privacy is not violated),
but it wasn't spelled out in earlier versions of Ada.
Wording Changes from Ada 2012
{
AI12-0065-1}
Corrigendum: Clarified the clarification added by AI05-0115-1,
as it turned out to not be that clear. Hopefully this version is better.
{
AI12-0140-1}
Correction: Clarified the constraints and properties that apply
to a designated subtype. This additional wording does not mean to change
existing practice.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe