Rationale for Ada 2012

John Barnes
Contents   Index   References   Search   Previous   Next 

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.

Contents   Index   References   Search   Previous   Next 
© 2011, 2012, 2013 John Barnes Informatics.
Sponsored in part by:
The Ada Resource Association:

    ARA
  AdaCore:


    AdaCore
and   Ada-Europe:

Ada-Europe