Rationale Update for Ada 2012
Chapter 4: Structure and visibility
One of the most dramatic changes in Ada 2012 concerns
subprogram parameters and is that functions can have parameters of all
modes. Other areas covered here include incomplete types and discriminants.
The following Ada Issues
cover this area:
Descendants of incomplete views
View conversions and out parameters passed by copy
An access definition should be a declarative region
Generic formal types and constrained partial views
Tag of the return object of a simple return expression
Incompatibility of hidden untagged record equality
Representation of untagged derived types
Freezing of renames-as-body
Incomplete views and access to class wide types
These changes can be grouped as follows.
A number of issues concern views. There are clarifications
of incomplete views (65
and omissions concerning view conversions (74
and constrained views (95
An amusing issue concerns the definition of a declarative
Miscellaneous issues concern renaming (132
untagged record equality (101
untagged derived types (109
and the tag of return objects (97
A curious situation
is discussed by AI-65
A type T3
can be a descendant of T1
but nevertheless inherits no characteristics of T1
because of an intervening type T2
package P is
type T1 is private; -- partial view
C: constant: T1;
type T1 is new Integer; -- complete view
C: constant := 37; -- my favourite number
package Q is
type T2 is new P.T1;
package P.Child is
type T3 is new Q.T2;
-- what can we do with T3 here?
In this example T3 is
derived from T2 and T2
is derived from T1. The fact that T2
is derived from Integer is not visible to
the declaration of T3. Nevertheless the conversion
rules allow a value of type T1 to be converted
to T3 in the private part of the child package.
But the fact that T3 is an integer type is
We say that T3 is effectively
a descendant of an incomplete view of T1.
(Note "effectively"; it's not technically an incomplete view
but behaves in some ways as if it were.) So we can convert C
but not 73 to type T3
in the private part of C.Child.
X: T3 := T3(P.C) -- OK
Y: T3 := T3(73); -- No, T3 is not visibly numeric!
It was meant to be like this in Ada 95; Ada 2005
meddled with it and Ada 2012 made a confusing "improvement".
Hopefully the clarifications made now will be the end of the story.
It is helpful to remember the distinction between
a partial view and an incomplete view.
A partial view is the view given by a private type
declaration in contrast to the full view given by the full declaration
in the private part. As in type T1
An incomplete view
is the view given by an incomplete declaration such as occurs with access
type Cell; -- incomplete view
type Link is access Cell:
type Cell is -- completion
The use of the concept of incomplete views was much
extended in Ada 2005 by the introduction of the limited with clause.
It was extended again in Ada 2012 by allowing incomplete types to be
completed by types other than access types and allowing incomplete views
There are many rules concerning access types that
designate incomplete views. AI-137
clarifies that they also apply to access to class wide types.
concerns an omission/confusion with regard to generic untagged formal
types and partial views. Briefly, within a generic body we assume the
worst as to whether or not a formal subtype has a constrained partial
view. In particular we assume that untagged formal private and derived
types do indeed have a constrained partial view.
As Ada has grown there
have been further lexical amusements such as functions returning access
to functions. Thus we can now have
type T is
access function( ... ) return
access function( ... ) return
access function( ... ) return ...
ad infinitum. To be
more specific the rules seem to prohibit
type T is
access function(A: Integer) return
access function(A: Float) return Boolean;
because here we have two instances of A
in the declarative region for T
. There is
no real reason why this should not be permitted so the definition of
declarative region is extended to include an access definition (AI-94
A somewhat different
topic is addressed by AI-74
and concerns parameters of mode out
which have always been the
source of troubles. The basic problem is that such parameters can become
undefined. Consider this simple procedure to find the two roots of a
procedure Quadratic(A, B, C: in Real;
Root_1, Root_2: out Real; OK: out Boolean) is
D: constant Real := B**2 – 4.0*A*C;
if D < 0.0 or A = 0.0 then
OK := False; return;
Root_1 := (–B + Sqrt(D)) / (2.0*A);
Root_2 := (–B – Sqrt(D)) / (2.0*A);
OK := True;
If the equation has
complex roots then no values are assigned to Root_1
and Root_2 so they are likely to contain rubbish.
So if we call Quadratic thus
Quadratic(AA, BB, CC, R1, R2, State);
then because of the copy in and out rules for parameters
of elementary types, the variables R1
which might have had respectable values
will now contain rubbish.
Of course if we had made the parameters Root_1
and Root_2 of mode in out then the
original values of R1 and R2
would have been retained if no assignments were made to Root_1
However, if we had been wise and used the Default_Value
aspect introduced in Ada 2012 thus
type Real is new Float
with Default_Value := 0.0;
then the behaviour is different. In this case Root_1
and Root_2 will behave essentially as if they
were of mode in out and will remain unchanged. Note carefully
that they will not take the default values of 0.0
and so the existing values of R1 and R2
will not be disturbed. Of course if we had declared a local variable
R_Temp of type Real
then it would take the initial value of 0.0.
This technique of initially copying in parameters
of mode out has existed in Ada for access types since Ada 83.
Remember that access types always have a default initial value of null
and so this copying in behaviour is identical. Incidentally, this copying
in is done "in the raw" without making any subtype checks such
as range constraints; again this follows the behaviour of access types.
Note also that the whole purpose of an out parameter is to give
it some value without concern for the original value of the actual parameter
and so gratuitously checking the original value of the actual could be
irritating if it raised an exception. Another point is that the default
value applies to the type and not to the subtype.
However, do remember that we cannot give a default
value to the predefined types such as Float
so this is a good reason for declaring our own types.
Other problems arise
when an actual parameter is a view conversion and this is the real topic
Consider the following simple example
procedure Inc(X: in out Integer) is
X := X + 1;
F := 3.14;
Remember that the behaviour is that the value of
F is converted to type Integer
(and thus becomes 3) and this is the initial
value of the parameter X which is then incremented
to 4 and finally converted to 4.0
and copied back into F. This is as in Ada
But problems arise
if the parameter is an out parameter and not an in out
procedure P(X: out My_Integer) is ...
Y: Long_Float := 1.0E20;
Now suppose we have given Default_Value
for My_Integer. An important goal of Default_Value
is to ensure that junk values do not arise. This is done by treating
out parameters essentially as in out parameters as illustrated
by Quadratic. But now we are in trouble because
we are unlikely to be able to convert the giant floating value Y
to the type My_Integer.
This problem is overcome by saying that if the aspect
Default_Value is given for the type of the
formal parameter then there must be an ancestor of both the target type
and the operand type of the view conversion and the operand type itself
must also have the aspect Default_Value. If
the conversion meets these requirements, then it is bound to work. Otherwise,
the view conversion (such as the example above) is made illegal.
concerns expression functions and freezing again (see the brief mention
in the previous Chapter). If we have an expression function such as
function F(...) return T is
(expression of subtype T);
then it can occur in
a renaming as body thus
function G( ... ) return T renames F;
This AI points out that this renaming freezes the
expression of the expression function F.
The redefining of equality
has always been a bother. Originally there were different rules for composition
of tagged and untagged types. The difference was removed in Ada 2012
in order to make composition more uniform. However, a quirk in the rules
meant that a hidden definition of equality for an untagged record type
package P is
type PT is private;
type PT is record ... end record; -- untagged
function "=" (L, R: PT) return Boolean;
was not permitted. This was a mistake and accordingly
this restriction is removed by AI-132
There are omissions regarding aspect specifications
and derived types. One of the advantages of the introduction of aspect
specifications is that they occur with the entity to which they apply.
This means that the traditional linear elaboration does not always apply
because the aspect might refer to things that have not yet been declared.
clarifies the situation with regard to the freezing of the representation
of untagged types.
addresses a minor error in the description of the tag of an object in
a return statement. The introduction of the extended return statement
where we have
return R: T do
needed clarification because T
might not be identical to the return type given in the function specification
(it might be a subtype; perhaps the function has an indefinite type and
the return is definite, perhaps classwide and specific and so on.) So
the rules were rewritten to cover the extended return. Unfortunately
the rules were written in a way that was incorrect for an old-fashioned
return statement. This is now put right.
© 2016 John Barnes Informatics.