6.5 Return Statements
Syntax
Name Resolution Rules
To be honest: The same applies to generic
functions.
Legality Rules
{
AI95-00318-02}
A return statement shall be within a callable construct,
and it
applies to the innermost callable construct or
extended_return_statement
that contains it. A return statement shall not be within a body that
is within the construct to which the return statement applies.
To be honest: {
AI12-0089-1}
The above also applies to generic subprograms, even though they are not
callable constructs. (An instance of a generic subprogram is a callable
construct, but not a generic subprogram itself.)
Reason: {
AI95-00318-02}
{
AI12-0022-1}
The requirement that a function body has to have at least one return
statement is a “helpful” restriction. There has been some
interest in lifting this restriction, or allowing a raise statement to
substitute for the return statement. However, there was enough interest
in leaving it as is that we decided not to change it. Note that for Ada
2012, Corrigendum 1, a return statement whose expression is a
raise_expression
can be given in
any function body (the
raise_expression
will match any type), so there is much less need to eliminate this rule.
{
AI12-0089-1}
Since a "function body" includes a generic function body, this
rule and all of the following Legality Rules apply to generic function
bodies as well as non-generic function bodies. This is true even though
a generic function is not a function.
Proof: {
AI12-0070-1}
The accessibility of such anonymous access types is defined in the Heart
of Darkness (aka
3.10.2).
{
AI05-0032-1}
If the result subtype of the function is class-wide, the accessibility
level of the type of the subtype defined by the
return_subtype_indication
shall not be statically deeper than that of the master that elaborated
the function body.
{
AI95-00416-01}
{
AI05-0032-1}
{
AI05-0051-1}
If the result subtype of the function is class-wide, the accessibility
level of the type of the
expression
(if any) of the return statement shall not be statically deeper than
that of the master that elaborated the function body.
Discussion: We use the type used by the
return statement rather than from the function return type since we want
to check whenever the return object has access discriminants, even if
the function return type doesn't have any (mostly for a class-wide type).
Static Semantics
Dynamic Semantics
{
AI95-00318-02}
{
AI95-00416-01}
{
AI05-0032-1}
For the execution of an
extended_return_statement,
the
subtype_indication
or
access_definition
is elaborated. This creates the nominal subtype of the return object.
If there is an
expression,
it is evaluated and converted to the nominal subtype (which might raise
Constraint_Error — see
4.6);
the return object is created and the converted value is assigned to the
return object. Otherwise, the return object is created and initialized
by default as for a stand-alone object of its nominal subtype (see
3.3.1).
If the nominal subtype is indefinite, the return object is constrained
by its initial value.
A check
is made that the value of the return object belongs to the function result
subtype. Constraint_Error is raised if this check fails.
Ramification: If the result type is controlled
or has a controlled part, appropriate calls on Initialize or Adjust are
performed prior to executing the
handled_sequence_of_statements,
except when the initial expression is an
aggregate
(which requires build-in-place with no call on Adjust).
{
AI05-0005-1}
If the return statement is left without resulting in a return (for example,
due to an exception propagated from the
expression
or the
handled_sequence_of_statements,
or a goto out of the
handled_sequence_of_statements),
if the return object has been created, it is finalized prior to leaving
the return statement. If it has not been created when the return statement
is left, it is not created or finalized.
{
AI05-0032-1}
Other rules ensure that the check required by this rule cannot fail unless
the function has a class-wide result subtype where the associated specific
subtype is constrained. In other cases, either the subtypes have to match
or the function's subtype is unconstrained and needs no checking.
Ramification: The conversion might raise
Constraint_Error — (see
4.6).
{
AI95-00318-02}
{
AI95-00416-01}
[If the return object has any parts that are tasks, the activation of
those tasks does not occur until after the function returns (see
9.2).]
Proof: This is specified by the rules
in
9.2.
Reason: Only the caller can know when
task activations should take place, as it depends on the context of the
call. If the function is being used to initialize the component of some
larger object, then that entire object must be initialized before any
task activations. Even after the outer object is fully initialized, task
activations are still postponed until the begin at the end of
the declarative part if the function is being used to initialize part
of a declared object.
Ramification: {
AI95-00318-02}
The first sentence is true even if the tag of the
expression
is different, which could happen if the
expression
were a view conversion or a dereference of an access value. Note that
for a limited type, because of the restriction to
aggregates
and function calls (and no conversions), the tag will already match.
Reason: {
AI95-00318-02}
The first rule ensures that a function whose result type is a specific
tagged type always returns an object whose tag is that of the result
type. This is important for dispatching on controlling result, and allows
the caller to allocate the appropriate amount of space to hold the value
being returned (assuming there are no discriminants).
The master check prevents the returned object
from outliving its type. Note that this check cannot fail for a specific
tagged type, as the tag represents the function's type, which necessarily
must be declared outside of the function.
We can't use the normal accessibility level
“deeper than” check here because we may have “incomparable”
levels if the masters belong to two different tasks. This can happen
when an accept statement calls a function declared in the enclosing task
body, and the function returns an object passed to it from the accept
statement, and this object was itself a parameter to the accept statement.
{
AI05-0073-1}
If the result subtype of the
function is defined by an
access_definition
designating a specific tagged type
T, a check is made that the
result value is null or the tag of the object designated by the result
value identifies
T.
Constraint_Error is raised
if this check fails.
Reason: This check is needed so that
dispatching on controlling access results works for tag-indeterminate
functions. If it was not made, it would be possible for such functions
to return an access to a descendant type, meaning the function could
return an object with a tag different than the one assumed by the dispatching
rules.
Paragraphs 9 through
20 were deleted.
{
AI95-00318-02}
{
AI95-00402-01}
{
AI95-00416-01}
{
AI05-0051-1}
If any part of the specific type of the return object of a function (or
coextension thereof) has one or more access discriminants whose value
is not constrained by the result subtype of the function, a check is
made that the accessibility level of the anonymous access type of each
access discriminant, as determined by the
expression
or the
return_subtype_indication
of the return statement, is not deeper than the level of the master of
the call (see
3.10.2). If this check fails,
Program_Error is raised.
This paragraph
was deleted.
Reason: The check prevents the returned
object (for a nonlimited type) from outliving the object designated by
one of its discriminants. The check is made on the values of the discriminants,
which may come from the
return_subtype_indication
(if constrained), or the
expression,
but it is never necessary to check both.
Implementation Note: {
AI05-0234-1}
The reason for saying “any part of the specific type” is
to simplify implementation. In the case of class-wide result objects,
this allows the testing of a simple flag in the tagged type descriptor
that indicates whether the specific type has any parts with access discriminants.
By basing the test on the type of the object rather than the object itself,
we avoid concerns about whether subcomponents in variant parts and of
arrays (which might be empty) are present.
Discussion: {
AI05-0234-1}
For a function with a class-wide result type, the access values that
need to be checked are determined by the tag of the return object. In
order to implement this accessibility check in the case where the tag
of the result is not known statically at the point of the return statement,
an implementation may need to somehow associate with the tag of a specific
tagged type an indication of whether the type has unconstrained access
discriminants (explicit or inherited) or has any subcomponents with such
discriminants. If an implementation is already maintaining a statically
initialized descriptor of some kind for each specific tagged type, then
an additional Boolean could be added to this descriptor.
{
AI05-0005-1}
{
AI05-0234-1}
Note that the flag should only be queried in the case where the result
object might have access discriminants that might have subtypes with
"bad" accessibility levels (as determined by the rules of
3.10.2
for determining the accessibility level of the type of an access discriminant
in the
expression
or
return_subtype_indication
of a return statement).
Thus, in a case
like
type Global is access T'Class;
function F (Ptr : Global) return T'Class is
begin
return Ptr.all;
end F;
there is no need
for a run-time accessibility check. While an object of T'Class "might
have" access discriminants, the accessibility of those potential
discriminants cannot be bad. The setting of the bit doesn't matter and
there is no need to query it.
On the other hand,
given
function F return T'Class is
Local : T'Class := ... ;
begin
return Local;
end F;
In this case,
a check would typically be required.
The need for including
subcomponents in this check is illustrated by the following example:
X : aliased Integer;
type Component_Type (Discrim : access Integer := X'Access)
is limited null record;
type Undiscriminated is record
Fld : Component_Type;
end record;
function F return Undiscriminated is
Local : aliased Integer;
begin
return X : Undiscriminated := (Fld => (Discrim => Local'Access)) do
Foo;
end return;
-- raises Program_Error after calling Foo.
end F;
Ramification: {
AI05-0234-1}
In the case where the tag of the result is not known statically at the
point of the return statement and the run-time accessibility check is
needed, discriminant values and array bounds play no role in performing
this check. That is, array components are assumed to have nonzero length
and components declared within variant parts are assumed to be present.
Thus, the check may be implemented simply by testing the aforementioned
descriptor bit and conditionally raising Program_Error.
Implementation Permissions
{
AI95-00416-01}
{
AI05-0050-1}
For a function call used to initialize a composite object with a constrained
nominal subtype or used to initialize a return object that is built in
place into such an object:
{
AI05-0050-1}
If the result subtype of the function is constrained, and conversion
of an object of this subtype to the subtype of the object being initialized
would raise Constraint_Error, then Constraint_Error may be raised before
calling the function.
{
AI05-0050-1}
If the result subtype of the function is unconstrained, and a return
statement is executed such that the return object is known to be constrained,
and conversion of the return object to the subtype of the object being
initialized would raise Constraint_Error, then Constraint_Error may be
raised at the point of the call (after abandoning the execution of the
function body).
Reason: {
AI95-00416-01}
{
AI05-0050-1}
Without such a permission, it would be very difficult to implement “built-in-place”
semantics. The intention is that the exception is raised at the same
point that it would have been raised without the permission; it should
not change handlers if the implementation switches between return-by-copy
and built-in-place. This means that the exception is not handleable within
the function, because in the return-by-copy case, the constraint check
to verify that the result satisfies the constraints of the object being
initialized happens after the function returns. This implies further
that upon detecting such a situation, the implementation may need to
simulate a goto to a point outside any local exception handlers prior
to raising the exception.
Ramification: {
AI95-00416-01}
{
AI05-0050-1}
These permissions do not apply in the case of an extended return object
with mutable discriminants. That's necessary because in that case a return
object can be created with the “wrong” discriminants and
then changed to the “right” discriminants later (but before
returning). We don't want this case raising an exception when the canonical
semantics will not do so.
{
AI05-0050-1}
It's still possible to write a program that will raise an exception using
this permission that would not in the canonical semantics. That could
happen if a return statement with the “wrong” discriminants
or bounds is abandoned (via an exception, or for an extended_return_statement,
via an exit or goto statement), and then a return statement with the
“right” discriminants or bounds is executed. The only solution
for this problem is to not have the permission at all, but this is too
unusual of a case to worry about the effects of the permission, especially
given the implementation difficulties for built-in-place objects that
this permission is intended to ease.
{
AI05-0050-1}
Note that the mutable-discriminant case only happens when built-in-place
initialization is optional. This means that any difficulties associated
with implementing built-in-place initialization without these permissions
can be sidestepped by not building in place.
Examples
Examples of return
statements:
return Key_Value(Last_Index); -- in a function body
{
AI95-00318-02}
return Node : Cell
do --
in a function body, see 3.10.1 for Cell
Node.Value := Result;
Node.Succ := Next_Node;
end return;
Incompatibilities With Ada 83
{
AI95-00318-02}
In Ada 95, if the result type of a function has a
part that is a task, then an attempt to return a local variable will
raise Program_Error. This is illegal in Ada 2005, see below. In Ada 83,
if a function returns a local variable containing a task, execution is
erroneous according to AI83-00867. However, there are other situations
where functions that return tasks (or that return a variant record only
one of whose variants includes a task) are correct in Ada 83 but will
raise Program_Error according to the new rules.
The rule change was made because there will
be more types (protected types, limited controlled types) in Ada 95 for
which it will be meaningless to return a local variable, and making all
of these erroneous is unacceptable. The current rule was felt to be the
simplest that kept upward incompatibilities to situations involving returning
tasks, which are quite rare.
Wording Changes from Ada 83
{
AI05-0299-1}
This subclause has been moved here from chapter 5, since it has mainly
to do with subprograms.
A function now creates an anonymous object.
This is necessary so that controlled types will work.
{
AI95-00318-02}
We have clarified that a return statement applies to a callable construct,
not to a callable entity.
{
AI95-00318-02}
There is no need to mention generics in the rules about where a return
statement can appear and what it applies to; the phrase “body of
a subprogram or generic subprogram” is syntactic, and refers exactly
to “
subprogram_body”.
Inconsistencies With Ada 95
{
AI95-0416-1}
{
AI05-0005-1}
{
AI05-0050-1}
Added an Implementation Permission allowing early
raising of Constraint_Error if the result cannot fit in the ultimate
object. This gives implementations more flexibility to do built-in-place
returns, and is essential for limited types (which cannot be built in
a temporary). However, it allows raising Constraint_Error in some cases
where it would not be raised if the permission was not used. See Inconsistencies
With Ada 2005 for additional changes. This case is potentially inconsistent
with Ada 95, but a compiler does not have to take advantage of these
permissions for any Ada 95 code, so there should be little practical
impact.
Incompatibilities With Ada 95
{
AI95-00318-02}
The entire business about return-by-reference types
has been dropped. Instead, the
expression
of a return statement of a limited type can only be an
aggregate
or
function_call
(see
7.5). This means that returning a global
object or
type_conversion,
legal in Ada 95, is now illegal. Such functions can be converted to use
anonymous access return types by adding
access in the function
definition and return statement, adding .
all in uses, and adding
aliased in the object declarations. This has the advantage of
making the reference return semantics much clearer to the casual reader.
We changed these rules so that functions, combined
with the new rules for limited types (
7.5),
can be used as build-in-place constructors for limited types. This reduces
the differences between limited and nonlimited types, which will make
limited types useful in more circumstances.
Extensions to Ada 95
{
AI95-00318-02}
The
extended_return_statement
is new. This provides a name for the object being returned, which reduces
the copying needed to return complex objects (including no copying at
all for limited objects). It also allows component-by-component construction
of the return object.
Wording Changes from Ada 95
{
AI95-00318-02}
The wording was updated to support anonymous access return subtypes.
{
AI95-00344-01}
{
AI95-00416-01}
Added accessibility checks to class-wide return statements. These checks
could not fail in Ada 95 (as all of the types had to be declared at the
same level, so the tagged type would necessarily have been at the same
level as the type of the object).
{
AI95-00402-01}
{
AI95-00416-01}
Added accessibility checks to return statements for types with access
discriminants. Since such types have to be limited in Ada 95, the
expression
of a return statement would have been illegal in order for this check
to fail.
Inconsistencies With Ada 2005
{
AI05-0050-1}
Correction: The Implementation Permission
allowing early raising of Constraint_Error was modified to remove the
most common of these cases from the permission (returning an object with
mutable discriminants, where the return object is created with one set
of discriminants and then changed to another). (The permission was also
widened to allow the early check for constrained functions when that
constraint is wrong.) However, there still is an unlikely case where
the permission would allow an exception to be raised when none would
be raised by the canonical semantics (when a return statement is abandoned).
These changes can only remove the raising of an exception (or change
the place where it is raised) compared to Ada 2005, so programs that
depend on the previous behavior should be very rare.
{
AI05-0051-1}
{
AI05-0234-1}
Correction: Accessibility checks for access discriminants now
depend on the master of the call rather than the point of declaration
of the function. This will result in cases that used to raise Program_Error
now running without raising any exception. This is technically inconsistent
with Ada 2005 (as defined by Amendment 1), but it is unlikely that any
real code depends on the raising of this exception.
{
AI05-0073-1}
Correction: Added a tag check for functions returning anonymous
access-to-tagged types, so that dispatching of tag-indeterminate function
works as expected. This is technically inconsistent with Ada 2005 (as
defined by Amendment 1), but as the feature in question was newly added
to Ada 2005, there should be little code that depends on the behavior
that now raises an exception.
Incompatibilities With Ada 2005
{
AI05-0053-1}
{
AI05-0277-1}
Correction: The
aliased keyword can
now only appear on extended return objects with an immutably limited
type. Other types would provide a way to get an aliased view of an object
that is not necessarily aliased, which would be very bad. This is incompatible,
but since the feature was added in Ada 2005, the keyword had no defined
meaning in Ada 2005 (a significant oversight), and most sensible uses
involve immutably limited types, it is unlikely that it appears meaningfully
in existing programs.
{
AI05-0103-1}
Correction: Added wording to require static matching for unconstrained
access types in extended return statements. This disallows adding or
omitting null exclusions, and adding access constraints, in the declaration
of the return object. While this is incompatible, the incompatible cases
in question are either useless (access constraints – the constraint
can be given on an
allocator
if necessary, and still must be given there even if given on the return
object) or wrong (null exclusions – null could be returned from
a function declared to be null excluding), so we expect them to be extremely
rare in practice.
Extensions to Ada 2005
{
AI05-0032-1}
Added wording to allow the
return_subtype_indication
to have a specific type if the return subtype of the function is class-wide.
Specifying the (specific) type of the return object is awkward without
this change, and this is consistent with the way
allocators
work.
Wording Changes from Ada 2005
{
AI05-0024-1}
Correction: Corrected the master check for tags since the masters
may be for different tasks and thus incomparable.
{
AI05-0058-1}
Correction: Corrected the wording defining returns for
extended_return_statements,
since leaving by an exit or goto is considered “normal” completion
of the statement.
Wording Changes from Ada 2012
{
AI05-0097-1}
Corrigendum: Clarified the wording so that it is clear where the
tag of the return object comes from. While a literal reading of the original
Ada 2012 rule could have caused some weird results (by using some nearby
subtype_indication
to provide the tag in the case of a
simple_return_statement,
such a reading would be so unlike the rest of the language that we do
not believe anyone would ever have thought it was intended. As such,
we do not believe any implementation ever did this wrong (at least because
of the old wording), and thus do not document this as a possible inconsistency.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe