Rationale for Ada 2012
1.3.5 Overview: General improvements
As well as the major features discussed above there
are also a number of improvements in various other areas.
We start with some
gentle stuff. Ada 95 introduced the package Ada
thus
package Ada is
pragma Pure(Ada);
end Ada;
However, a close reading of the RM revealed that
poor Ada is illegal since the pragma Pure
is not in one of the allowed places for a pragma. Pragmas are allowed
in the places where certain categories are allowed but not in place
of them. In the case of a package specification the constructs are
basic declarative items, but "items" were not one of the allowed
things. This has been changed to keep Ada
legal.
A related change concerns
a sequence of statements. In a construction such as
if B then
This;
else
That;
end if;
there must be at least
one statement in each branch so if we don't want any statements then
we have to put a null statement. If we want a branch that is just a pragma
Assert then we have to put a null statement
as well thus
if B then
pragma Assert(...); null;
end if;
This is really irritating and so the rules have been
changed to permit a pragma in place of a statement in a sequence of statements.
This and the problem with the package Ada
are treated as Binding Interpretations which means that they apply to
Ada 2005 as well.
A similar change concerns the position of labels.
It is said that gotos are bad for you. However, gotos are useful for
quitting an execution of a loop and going to the end in order to try
the next iteration. Thus
for I in
... loop
...
if this-one-no-good then goto End_Of_Loop; end if;
...
<<End_Of_Loop>> null;
-- try another iteration
end loop;
Ada provides no convenient way of doing this other
than by using a goto statement. Remember that exit transfers control
out of the loop. The possibility of a continue statement as in some other
languages was discussed but it was concluded that this would obscure
the transfer of control. The great thing about goto is that the
label sticks out like a sore thumb. Indeed, a survey of the code in a
well known compiler revealed an orgy of uses of this handy construction.
However, it was decided that having to put null
was an ugly nuisance and so the syntax of Ada 2012 has been changed to
permit the label to come right at the end.
There is a significant
extension to the syntax of loops used for iteration. This arose out of
a requirement to make iteration over containers easier (as outlined in
Section
1.3.6) but the general ideas can
be illustrated with an array. Consider
for K in Table'Range loop
Table(K) := Table(K) + 1;
end loop;
This can now be written
as
for T of Table loop
T := T + 1;
end loop;
The entity T is a sort
of generalized reference and hides the indexing. This mechanism can also
be used with multidimensional arrays in which case just one loop replaces
a nested set of loops.
A minor problem has arisen with the use of tags and
Generic_Dispatching_Constructor. There is
no way of discovering whether a tag represents an abstract type other
than by attempting to create an object of the type which then raises
Tag_Error; this is disgusting. Accordingly,
a new function
function Is_Abstract(T: Tag) return Boolean;
is added to the package Ada.Tags.
There were many changes to access types in Ada 2005
including the wide introduction of anonymous access types. Inevitably
some problems have arisen.
The first problem is
with the accessibility of stand-alone objects of anonymous access types
such as
A: access T;
Without going into details, it turns out that such
objects are not very useful unless they carry the accessibility level
of their value in much the same way that access parameters carry the
accessibility level of the actual parameter. They are therefore modified
to do this.
Programmers have always moaned about the need for
many explicit conversions in Ada. Accordingly, implicit conversions from
anonymous access types to named access types are now permitted provided
the explicit conversion is legal. The idea is that the need for an explicit
conversion with access types should only arise if the conversion could
fail. A curious consequence of this change is that a preference rule
is needed for the equality of anonymous access types.
An issue regarding allocators concerns their alignment.
It will be recalled that when implementing a storage pool, the attribute
Max_Size_In_Storage_Units is useful since
it indicates the maximum size that could be requested by a call of Allocate.
Similarly, the new attribute Max_Alignment_For_Allocation
indicates the maximum alignment that could be requested.
Another problem is
that allocators for anonymous access types cause difficulties in some
areas. Rather than forbidding them completely a new restriction identifier
is added so that we can write
pragma Restrictions(No_Anonymous_Allocators);
Another new restriction
is
pragma Restrictions(No_Standard_Allocators_After_Elaboration);
This can be used to ensure that once the main subprogram
has started no further allocation from standard storage pools is permitted.
This prevents a long lived program suffering from rampant heap growth.
However, this does not prevent allocation from user-defined
storage pools. To enable users to monitor such allocation, additional
functions are provided in
Ada.Task_Identification,
namely
Environment_Task (returns the
Task_Id
of the environment task) and
Activation_Is_Complete
(returns a Boolean result indicating whether a particular task has finished
activation).
A new facility is the ability to define subpools
using a new package
System.Storage_Pools.Subpools.
A subpool is a separately reclaimable part of a storage pool and is identified
by a subpool handle name. On allocation, a handle name can be given.
Further control over
the use of storage pools is provided by the ability to define our own
default storage pool.
Thus we
can write
pragma Default_Storage_Pool(My_Pool);
and then all allocation
within the scope of the pragma will be from
My_Pool
unless a different specific pool is given for a type.
This
could be done using the aspect
Storage_Pool
as expected
type Cell_Ptr is access Cell
with Storage_Pool => Cell_Ptr_Pool;
A pragma Default_Storage_Pool
can be overridden by another one so that for example all allocation in
a package (and its children) is from another pool. The default pool can
be specified as null thus
pragma Default_Storage_Pool(null);
and this prevents any allocation from standard pools.
Note that coextensions and allocators as access parameters
may nevertheless be allocated on the stack. This can be prevented (somewhat
brutally) by the following restrictions
pragma Restrictions(No_Coextensions);
pragma Restrictions(No_Access_Parameter_Allocators);
A number of other restrictions
have also been added. The introduction of aspects logically requires
some new restrictions to control their use. Thus by analogy with
No_Implementation_Pragmas,
we can write
pragma Restrictions(No_Implementation_Aspect_Specifications);
and this prevents the
use of any implementation-defined aspect specifications. The use of individual
aspects such as
Default_Value can be prevented
by
pragma Restrictions(No_Specification_of_Aspect => Default_Value);
Implementations and
indeed users are permitted to add descendant units of
Ada,
System and
Interfaces
such as another child of
Ada.Containers. This
can be confusing for maintainers since they may be not aware that they
are using non-standard packages. The new restriction
pragma Restrictions(No_Implementation_Units);
prevents the use of such units.
In a similar vein,
there is also
pragma Restrictions(No_Implementation_Identifiers);
and this prevents the use of additional identifiers
declared in packages such as System.
A blanket restriction
can be imposed by writing
pragma Profile(No_Implementation_Extensions);
and this is equivalent
to the following five restrictions
No_Implementation_Aspect_Specifications,
No_Implementation_Attributes,
No_Implementation_Identifiers,
No_Implementation_Pragmas,
No_Implementation_Units.
Finally, the issue of composability of equality has
been revisited. In Ada 2005, tagged record types compose but untagged
record types do not. If we define a new type (a record type, array type
or a derived type) then equality is defined in terms of equality for
its various components. However, the behaviour of components which are
records is different in Ada 2005 according to whether they are tagged
or not. If a component is tagged then the primitive operation is used
(which might have been redefined), whereas for an untagged type, predefined
equality is used even though it might have been overridden. This is a
bit surprising and so has been changed in Ada 2012 so that all record
types behave the same way and use the primitive operation. This is often
called composability of equality so that we can say that in Ada 2012,
record types always compose for equality. Remember that this only applies
to records; components which are of array types and elementary types
always use predefined equality.
© 2011, 2012, 2013 John Barnes Informatics.
Sponsored in part by: