7.5 Limited Types
1/2
{
AI95-00287-01}
[A limited type is (a view of) a type for which
copying
(such as for an assignment_statement) the
assignment operation is not allowed. A nonlimited type is a (view
of a) type for which
copying the
assignment operation is allowed.]
1.a
Discussion: The concept of the value
of a limited type is difficult to define, since the abstract value of
a limited type often extends beyond its physical representation. In some
sense, values of a limited type cannot be divorced from their object.
The value is the object.
1.b/2
{
AI95-00318-02}
In Ada 83, in the two places where limited types were defined by the
language, namely tasks and files, an implicit level of indirection was
implied by the semantics to avoid the separation of the value from an
associated object. In Ada 95, most limited types are passed by reference,
and even return-ed by reference.
In Ada 2005, most
limited types are built-in-place upon return, rather than returned by
reference. Thus the object “identity” is part of the logical
value of most limited types.
1.c/2
To be honest: {
AI95-00287-01}
{
AI95-00419-01}
For a limited partial view whose full view is nonlimited,
copying assignment
is possible on parameter passing and function return. To prevent any
copying whatsoever, one should make both the partial
and full
views limited.
1.d/2
Glossary entry: {Limited type}
A limited type is (a view of) a type for
which copying (such as in an assignment_statement) the
assignment operation is not allowed. A nonlimited type is a (view
of a) type for which copying the
assignment operation is allowed.
Legality Rules
2/2
{
AI95-00419-01}
If a tagged record type has any limited components, then the reserved
word
limited shall appear in its
record_type_definition.
[If the reserved word limited appears in the definition of a derived_type_definition,
its parent type and any progenitor interfaces shall be limited.]
2.a.1/2
Proof: {
AI95-00419-01}
The rule about the parent type being required to
be limited can be found in 3.4. Rules about
progenitor interfaces can be found in 3.9.4,
specifically, a nonlimited interface can appear only on a nonlimited
type. We repeat these rules here to gather these scattered rules in one
obvious place.
2.a
Reason: This
prevents tagged limited types from becoming nonlimited. Otherwise, the
following could happen:
2.b
package P is
type T is limited private;
type R is tagged
record -- Illegal!
-- This should say “limited record”.
X : T;
end record;
private
type T is new Integer; -- R becomes nonlimited here.
end P;
2.c/2
package Q is
type R2(Access_Discrim : access ...) is new R with
record
Y : Some_Task_Type;
end record;
end Q;
2.d/2
{
AI95-00230-01}
If the above were legal, then assignment would be defined for R'Class
in the body of P, which is bad news, given
the
access discriminant and the task.
2.1/2
{
AI95-00287-01}
{
AI95-00318-02}
In the following contexts, an expression
of a limited type is not permitted unless it is an aggregate,
a function_call, or a parenthesized expression
or qualified_expression whose operand is permitted
by this rule:
2.2/2
- the initialization
expression of an object_declaration
(see 3.3.1)
2.3/2
- the default_expression
of a component_declaration (see 3.8)
2.4/2
- the expression
of a record_component_association (see 4.3.1)
2.5/2
- the expression
for an ancestor_part of an extension_aggregate
(see 4.3.2)
2.6/2
- an expression
of a positional_array_aggregate or the expression
of an array_component_association (see 4.3.3)
2.7/2
- the qualified_expression
of an initialized allocator (see 4.8)
2.8/2
- the expression
of a return statement (see 6.5)
2.9/2
- the default_expression
or actual parameter for a formal object of mode in (see 12.4)
2.e/2
Discussion: All
of these contexts normally require copying; by restricting the uses as
above, we can require the new object to be built-in-place.
Static Semantics
3/2
{
AI95-00419-01}
{limited type} A
type is
limited if it is
a descendant of
one of the following:
4/2
4.a
Ramification: Note that there is always
a “definition,” conceptually, even if there is no syntactic
category called “..._definition”.
4.b/2
{
AI95-00419-01}
This includes interfaces of the above kinds, derived
types with the reserved word limited, as well as task and protected
types.
5/2
- This
paragraph was deleted.{AI95-00419-01}
a task or protected type;
6/2
6.1/2
- {AI95-00419-01}
a derived type whose parent is limited and is not
an interface.
6.a/2
Ramification: {
AI95-00419-01}
Limitedness is not inherited from interfaces; it
must be explicitly specified when the parent is an interface.
6.b/2
To be honest: {
AI95-00419-01}
A derived type can become nonlimited if limited
does not appear and the derivation takes place in the visible part of
a child package, and the parent type is nonlimited as viewed from the
private part or body of the child package.
6.c/2
Reason: {
AI95-00419-01}
We considered a rule where limitedness was always
inherited from the parent for derived types, but in the case of a type
whose parent is an interface, this meant that the first interface is
treated differently than other interfaces. It also would have forced
users to declare dummy nonlimited interfaces just to get the limitedness
right. We also considered a syntax like not limited to specify
nonlimitedness when the parent was limited, but that was unsavory. The
rule given is more uniform and simpler to understand.
6.d/2
{
AI95-00419-01}
The rules for interfaces are asymmetrical, but
the language is not: if the parent interface is limited, the presence
of the word limited determines the limitedness, and nonlimited
progenitors are illegal by the rules in 3.9.4.
If the parent interface is nonlimited, the word limited is illegal
by the rules in 3.4. The net effect is that
the order of the interfaces doesn't matter.
7
{nonlimited type}
Otherwise, the type is nonlimited.
8
[There are no predefined equality operators for a
limited type.]
Implementation Requirements
8.1/2
{
AI95-00287-01}
{
AI95-00318-02}
For an aggregate of
a limited type used to initialize an object as allowed above, the implementation
shall not create a separate anonymous object for the aggregate.
For a function_call of a type with a part
that is of a task, protected, or explicitly limited record type that
is used to initialize an object as allowed above, the implementation
shall not create a separate return object (see 6.5) for the function_call.
The aggregate or function_call
shall be constructed directly in the new object.
8.a/2
Discussion: {
AI95-00318-02}
For a function_call,
we only require build-in-place{build-in-place [partial]}
for a limited type that would have been a return-by-reference
type in Ada 95. We do this because we want to minimize disruption to
Ada 95 implementations and users.
9/2
14 {
AI95-00287-01}
{
AI95-00318-02}
While it is allowed to write initializations of
limited objects, such initializations never copy a limited object. The
source of such an assignment operation must be an aggregate
or function_call, and such aggregates
and function_calls must be built directly
in the target object. The following are
consequences of the rules for limited types:
9.a/2
To be honest: This
isn't quite true if the type can become nonlimited (see below); function_calls
only are required to be build-in-place for “really” limited
types.
Paragraphs
10 through 15 were deleted.
10/2
- {AI95-00287-01}
An initialization expression is not allowed in
an object_declaration if the type of the object
is limited.
11/2
- {AI95-00287-01}
A default expression is not allowed in a component_declaration
if the type of the record component is limited.
12/2
- {AI95-00287-01}
An initialized allocator is not allowed if the
designated type is limited.
13/2
- {AI95-00287-01}
A generic formal parameter of mode in must
not be of a limited type.
14/2
15 {
AI95-00287-01}
Aggregates are not available
for a limited composite type. Concatenation is not available for a limited
array type.
15/2
16 {
AI95-00287-01}
The rules do not exclude a default_expression
for a formal parameter of a limited type; they do not exclude a deferred
constant of a limited type if the full declaration of the constant is
of a nonlimited type.
16
17
{become
nonlimited} {nonlimited
type (becoming nonlimited)} {limited
type (becoming nonlimited)} As illustrated
in
7.3.1, an untagged limited type can become
nonlimited under certain circumstances.
16.a
Ramification: Limited private types do
not become nonlimited; instead, their full view can be nonlimited, which
has a similar effect.
16.b
It is important to remember that a single nonprivate
type can be both limited and nonlimited in different parts of its scope.
In other words, “limited” is a property that depends on where
you are in the scope of the type. We don't call this a “view property”
because there is no particular declaration to declare the nonlimited
view.
16.c
Tagged types never become nonlimited.
Examples
17
Example of a package
with a limited type:
18
package IO_Package is
type File_Name is limited private;
19
procedure Open (F : in out File_Name);
procedure Close(F : in out File_Name);
procedure Read (F : in File_Name; Item : out Integer);
procedure Write(F : in File_Name; Item : in Integer);
private
type File_Name is
limited record
Internal_Name : Integer := 0;
end record;
end IO_Package;
20
package body IO_Package is
Limit : constant := 200;
type File_Descriptor is record ... end record;
Directory : array (1 .. Limit) of File_Descriptor;
...
procedure Open (F : in out File_Name) is ... end;
procedure Close(F : in out File_Name) is ... end;
procedure Read (F : in File_Name; Item : out Integer) is ... end;
procedure Write(F : in File_Name; Item : in Integer) is ... end;
begin
...
end IO_Package;
21
18 Notes on the example: In the
example above, an outside subprogram making use of IO_Package may obtain
a file name by calling Open and later use it in calls to Read and Write.
Thus, outside the package, a file name obtained from Open acts as a kind
of password; its internal properties (such as containing a numeric value)
are not known and no other operations (such as addition or comparison
of internal names) can be performed on a file name. Most importantly,
clients of the package cannot make copies of objects of type File_Name.
22
This example is characteristic of any case where complete
control over the operations of a type is desired. Such packages serve
a dual purpose. They prevent a user from making use of the internal structure
of the type. They also implement the notion of an encapsulated data type
where the only operations on the type are those given in the package
specification.
23/2
{
AI-00318-02}
The fact that the full view of File_Name is explicitly declared
limited
means that parameter passing
and function return
will always be by reference
and function
results will always be built directly in the result object (see
6.2 and
6.5).
Extensions to Ada 83
23.a
{
extensions to Ada 83}
The
restrictions in RM83-7.4.4(4), which disallowed
out parameters
of limited types in certain cases, are removed.
Wording Changes from Ada 83
23.b
Since limitedness and privateness are orthogonal
in Ada 95 (and to some extent in Ada 83), this is now its own clause
rather than being a subclause of
7.3, “
Private
Types and Private Extensions”.
Extensions to Ada 95
23.c/2
{
AI95-00287-01}
{
AI95-00318-02}
{extensions to Ada 95} Limited
types now have an assignment operation, but its use is restricted such
that all uses are build-in-place. This is accomplished by restricting
uses to aggregates and function_calls.
Aggregates were not allowed to have a limited
type in Ada 95, which causes a compatibility issue discussed in 4.3,
“Aggregates”. Compatibility issues
with return statements for limited function_calls
are discussed in 6.5, “Return
Statements”.
Wording Changes from Ada 95
23.d/2
{
AI95-00411-01}
{
AI95-00419-01}
Rewrote the definition of limited to ensure that
interfaces are covered, but that limitedness is not inherited from interfaces.
Derived types that explicitly include limited are now also covered.