6.4.1 Parameter Associations
[
A parameter association defines
the association between an actual parameter and a formal parameter.]
Language Design Principles
The parameter passing rules for
out parameters
are designed to ensure that the parts of a type that have implicit initial
values (see
3.3.1) don't become “de-initialized”
by being passed as an
out parameter.
{
AI05-0142-4}
For explicitly aliased parameters of functions, we will ensure at the
call site that a part of the parameter can be returned as part of the
function result without creating a dangling pointer. We do this with
accessibility checks at the call site that all actual objects of explicitly
aliased parameters live at least as long as the function result; then
we can allow them to be returned as access discriminants or anonymous
access results, as those have the master of the function result.
Name Resolution Rules
To be honest: {
AI05-0118-1}
For positional parameters, the “corresponding position” is
calculated after any transformation of prefixed views.
To be honest: The corresponding
default_expression
is the one of the corresponding formal parameter in the profile of the
view denoted by the
name
or
prefix
of the call.
If the mode is
in, the actual is interpreted
as an
expression;
otherwise, the actual is interpreted only as a
name,
if possible.
Ramification: {
AI12-0005-1}
This formally resolves the ambiguity present in the syntax rule for
explicit_actual_parameter.
This matters as an
expression
that is a
name
is evaluated and represents a value while a
name
by itself can be an object; if the mode is not
in, we want the
parameter to interpreted as an object. Note that we don't actually require
that the actual be a
name
if the mode is not
in; we do that below.
{
AI12-0005-1}
This wording uses "interpreted as" rather than "shall
be" so that this rule is not used to resolve overloading; it is
solely about evaluation as described above. We definitely do not want
to allow oddities like the presence of parentheses requiring the selection
of an
in formal parameter as opposed to an otherwise matching
in out parameter.
Legality Rules
If the mode is
in out or
out, the actual
shall be a
name
that denotes a variable.
Discussion: {
AI12-0005-1}
We no longer need “or a
type_conversion
whose argument is the
name
of a variable”, because a
type_conversion
is now a
name,
and a view conversion of a variable is a variable while any other conversion
(which should not be legal here) is a constant.
Reason: The
requirement that the actual be a (variable)
name
is not an overload resolution rule, since we don't want the difference
between
expression
and
name to
be used to resolve overloading. For example:
procedure Print(X : in Integer; Y : in Boolean := True);
procedure Print(Z : in out Integer);
. . .
Print(3); -- Ambiguous!
The above call to Print is ambiguous even though
the call is not compatible with the second Print which requires an actual
that is a (variable)
name
(“3” is an
expression,
not a
name).
This requirement is a legality rule, so overload resolution fails before
it is considered, meaning that the call is ambiguous.
neither the target type nor the operand type has
the Default_Value aspect specified; or
both the target type and the operand type shall
have the Default_Value aspect specified, and there shall exist a type
(other than a root numeric type) that is an ancestor of both the target
type and the operand type.
{
AI12-0074-1}
{
AI12-0159-1}
In addition to the places where Legality Rules normally
apply (see
12.3), these rules also apply in
the private part of an instance of a generic unit.
Reason: These rules are needed in order
to ensure that a well-defined parameter value is passed.
{
AI05-0102-1}
{
AI05-0142-4}
If the formal parameter is an explicitly aliased parameter, the type
of the actual parameter shall be tagged or the actual parameter shall
be an aliased view of an object. Further, if the formal parameter subtype
F is untagged:
the subtype F shall statically match the
nominal subtype of the actual object; or
the subtype F shall be unconstrained, discriminated
in its full view, and unconstrained in any partial view.
Ramification: Tagged objects (and tagged
aggregates
for
in parameters) do not need to be aliased. This matches the
behavior of unaliased formal parameters of tagged types, which allow
'Access to be taken of the formal parameter regardless of the form of
the actual parameter.
Reason: We need the subtype check on
untagged actual parameters so that the requirements of 'Access are not
lost. 'Access makes its checks against the nominal subtype of its prefix,
and parameter passing can change that subtype. But we don't want this
parameter passing to change the objects that would be allowed as the
prefix of 'Access. This is particularly important for arrays, where we
don't want to require any additional implementation burden.
Discussion: {
AI12-0095-1}
{
AI12-0005-1}
We assume the worst in a generic body regarding whether a formal subtype
has a constrained partial view; specifically, in a generic body a discriminated
subtype is considered to have a constrained partial view if it is a descendant
of an untagged generic formal private or derived type (see
12.5.1
for the formal definition of this rule).
{
AI12-0095-1}
In addition to the places where Legality Rules normally
apply (see
12.3), these rules also apply in
the private part of an instance of a generic unit.
{
AI05-0142-4}
{
AI05-0234-1}
In a function call, the accessibility level of the actual object for
each explicitly aliased parameter shall not be statically deeper than
the accessibility level of the master of the call (see
3.10.2).
Discussion: Since explicitly aliased
parameters are either tagged or required to be objects, there is always
an object (possibly anonymous) to talk about. This is discussing the
static accessibility level of the actual object; it does not depend on
any runtime information (for instance when the actual object is a formal
parameter of another subprogram, it does not depend on the actual parameter
of that other subprogram).
Ramification: {
AI12-0095-1}
This accessibility check (and its dynamic cousin as well) can only fail
if the master of the function call (which is defined in the Heart of
Darkness, or
3.10.2 if you prefer) is different
than the master directly enclosing the call. The most likely place where
this will occur is in the initializer of an
allocator;
in almost all other cases this check will always pass.
both
names
statically denote the same stand-alone object or parameter; or
both
names
are dereferences (implicit or explicit) and the dereferenced
names
are known to denote the same object; or
both
names
are
indexed_components,
their
prefixes
are known to denote the same object, and each of the pairs of corresponding
index values are either both static expressions with the same static
value or both
names
that are known to denote the same object; or
both
names
are
slices,
their
prefixes
are known to denote the same object, and the two
slices
have statically matching index constraints; or
one of the two
names
statically denotes a renaming declaration whose renamed
object_name
is known to denote the same object as the other, the
prefix
of any dereference within the renamed
object_name
is not a variable, and any
expression
within the renamed
object_name
contains no references to variables nor calls on nonstatic functions.
Reason: This
exposes known renamings of slices, indexing, and so on to this definition.
In particular, if we have
C : Character renames S(1);
then C and S(1)
are known to denote the same object.
We need the requirement
that no variables occur in the
prefixes
of dereferences and in (index)
expressions
of the renamed object in order to avoid problems from later changes to
those parts of renamed names. Consider:
type Ref is access Some_Type;
Ptr : Ref := new Some_Type'(...);
X : Some_Type renames Ptr.all;
begin
Ptr := new Some_Type'(...);
P (Func_With_Out_Params (Ptr.all), X);
X and Ptr.all
should not be known to denote the same object, since they denote different
allocated objects (and this is not an unreasonable thing to do).
To be honest:
The exclusion of variables from renamed object_names is not enough
to prevent altering the value of the name or expression by another access
path. For instance, both in parameters passed by reference and
access-to-constant values can designate variables. For the intended use
of "known to be the same object", this is OK; the modification
via another access path is very tricky and it is OK to reject code that
would be buggy except for the tricky code. Assuming Element is an elementary
type, consider the following example:
Global : Tagged_Type;
procedure Foo (Param : in Tagged_Type := Global) is
X : Element renames Some_Global_Array (Param.C);
begin
Global.C := Global.C + 1;
Swap (X, Some_Global_Array (Param.C));
The rules will flag the call of procedure Swap
as illegal, since X and Some_Global_Array (Parameter.C) are known to
denote the same object (even though they will actually represent different
objects if Param = Global). But this is only incorrect if the parameter
actually is Global and not some other value; the error could exist for
some calls. So this flagging seems harmless.
Similar examples can be constructed using stand-alone
composite constants with controlled or immutably limited components,
and (as previously noted) with dereferences of access-to-constant values.
Even when these examples flag a call incorrectly, that call depends on
very tricky code (modifying the value of a constant); the code is likely
to confuse future maintainers as well and thus we do not mind rejecting
it.
Discussion: Whether or not
names
or
prefixes
are known to denote the same object is determined statically. If the
name contains some dynamic portion other than a dereference,
indexed_component,
or
slice,
it is not "known to denote the same object".
These rules make no attempt to handle slices
of objects that are known to be the same when the slices have dynamic
bounds (other than the trivial case of bounds being defined by the same
subtype), even when the bounds could be proven to be the same, as it
is just too complex to get right and these rules are intended to be conservative.
Ramification:
"Known to denote the same object" is intended to be an
equivalence relationship, that is, it is reflexive, symmetric, and transitive.
We believe this follows from the rules. For instance, given the following
declarations:
S : String(1..10);
ONE : constant Natural := 1;
R : Character renames S(1);
the names R and
S(1) are known to denote the same object by the sixth bullet, and S(1)
and S(ONE) are known to denote the same object by the fourth bullet,
so using the sixth bullet on R and S(ONE), we simply have to test S(1)
vs. S(ONE), which we already know denote the same object.
The two
names
are known to denote the same object; or
One of the two
names
statically denotes a renaming declaration whose renamed
object_name
is known to refer to the same object as the other
name.
Reason: This ensures that names Prefix.Comp
and Prefix are known to refer to the same object for the purposes of
the rules below. This intentionally does not include dereferences; we
only want to worry about accesses to the same object, and a dereference
changes the object in question. (There is nothing shared between an access
value and the object it designates.)
{
AI05-0144-2}
If a call
C has two or more parameters of mode
in out or
out that are of an elementary type, then the call is legal only
if:
{
AI12-0216-1}
{
AI12-0324-1}
For each
name
N denoting an object of an elementary type that is passed as a
parameter of mode
in out or
out to the call
C, there
is no other
name
among the other parameters of mode
in out or
out to
C
that is known to denote the same object.
To be honest: This means visibly
an elementary type; it does not include partial views of elementary types
(partial views are always composite). That's necessary to avoid having
Legality Rules depend on the contents of the private part.
{
AI05-0144-2}
If a construct
C has two or more direct constituents that are
names or
expressions
whose evaluation may occur in an arbitrary order, at least one of which
contains a function call with an
in out or
out parameter,
then the construct is legal only if:
Ramification: All of the places where
the language allows an arbitrary order can be found by looking in the
index under "arbitrary order, allowed". Note that this listing
includes places that don't involve
names
or
expressions
(such as checks or finalization).
For each name
N that is passed as a parameter
of mode
in out or
out to some inner function call
C2
(not including the construct
C itself), there is no other
name
anywhere within a direct constituent of the construct
C other
than the one containing
C2, that is known to refer to the same
object.
Ramification: This requirement cannot
fail for a procedure or entry call alone; there must be at least one
function with an in out or out parameter called as part
of a parameter expression of the call in order for it to fail.
Reason: These rules prevent obvious cases
of dependence on the order of evaluation of
names
or
expressions.
Such dependence is usually a bug, and in any case, is not portable to
another implementation (or even another optimization setting).
In the case that the top-level construct C is
a call, these rules do not require checks for most
in out parameters,
as the rules about evaluation of calls prevent problems. Similarly, we
do not need checks for short circuit operations or other operations with
a defined order of evaluation. The rules about arbitrary order (see
1.1.4)
allow evaluating parameters and writing parameters back in an arbitrary
order, but not interleaving of evaluating parameters of one call with
writing parameters back from another — that would not correspond
to any allowed sequential order.
For a call, any
default_expression
evaluated as part of the call is considered part of the call.
Ramification: We do not check expressions
that are evaluated only because of a component initialized by default
in an aggregate (via <>).
Dynamic Semantics
The actual parameter is first evaluated.
For an access parameter, the
access_definition
is elaborated, which creates the anonymous access type.
For a parameter [(of any mode)] that is passed
by reference (see
6.2), a view conversion of
the actual parameter to the nominal subtype of the formal parameter is
evaluated, and the formal parameter denotes that conversion.
Discussion: We are always allowing sliding,
even for [in] out by-reference parameters.
For an
in or
in out
parameter that is passed by copy (see
6.2),
the formal parameter object is created, and the value of the actual parameter
is converted to the nominal subtype of the formal parameter and assigned
to the formal.
Ramification: The conversion mentioned
here is a value conversion.
For an out
parameter that is passed by copy, the formal parameter object is created,
and:
{
AI05-0153-3}
{
AI05-0196-1}
{
AI12-0378-1}
For an access type, the formal parameter is initialized from the value
of the actual, without checking whether the value satisfies any constraints,
predicates, or null exclusions, but including any[ dynamic] accessibility
checks associated with a conversion to the type of the formal parameter.
Reason: This preserves the Language Design
Principle that an object of an access type is always initialized with
a “reasonable” value.
Ramification: {
AI12-0378-1}
The permission to pass
null (see below) can be used in any case
where an accessibility check could fail, rather than making a check.
Reason: This preserves the Language Design
Principle that all objects of a type with an implicit initial value are
initialized. This is important so that a programmer can guarantee that
all objects of a scalar type have a valid value with a carefully chosen
Default_Value.
Implementation Note: This rule means
that out parameters of a subtype T with a specified Default_Value
need to be large enough to support any possible value of the base type
of T. In contrast, a type that does not have a Default_Value only
need support the size of the subtype (since no values are passed in).
{
AI12-0333-1}
For a composite type with discriminants or that has implicit initial
values for any subcomponents (see
3.3.1),
the behavior is as for an
in out parameter passed by copy[, except
that no predicate check is performed].
Reason: This ensures that no part of
an object of such a type can become “de-initialized” by being
part of an out parameter.
Ramification: This includes an array
type whose component type is an access type, and a record type with a
component that has a
default_expression,
among other things.
Proof: {
AI12-0333-1}
No predicate check follows from the definition of subtype conversion
in
4.6.
{
AI12-0439-1}
For any other type, the formal parameter is uninitialized. If composite,
a view conversion of the actual parameter to the nominal subtype of the
formal is evaluated [(which can raise Constraint_Error)], and the actual
subtype of the formal is that of the view conversion. If elementary,
the actual subtype of the formal is given by its nominal subtype.
Ramification: {
AI05-0228-1}
This case covers scalar types that do not have Default_Value specified,
and composite types whose subcomponent's subtypes do not have any implicit
initial values. The view conversion for composite types ensures that
if the lengths don't match between an actual and a formal array parameter,
the Constraint_Error is raised before the call, rather than after.
{
AI12-0377-1}
Furthermore, if the type is a scalar type, and the actual parameter is
a view conversion, then Program_Error is raised if either the target
or the operand type has the Default_Value aspect specified, unless they
both have the Default_Value aspect specified, and there is a type (other
than a root numeric type) that is an ancestor of both the target type
and the operand type.
Discussion: This can only occur in the
body of an instance of a generic unit. Legality Rules will catch all
other cases. Implementations that macro-expand generics can always detect
this case when the enclosing instance body is expanded.
{
AI05-0142-4}
{
AI05-0234-1}
In a function call, for each explicitly aliased parameter, a check is
made that the accessibility level of the master of the actual object
is not deeper than that of the master of the call (see
3.10.2).
Ramification: If the actual object to
a call C is a formal parameter of some function call F,
no dynamic check against the master of the actual parameter of F
is necessary. Any case which could fail the dynamic check is already
statically illegal (either at the call site of F, or at the call
site C). This is important, as it would require nasty distributed
overhead to accurately know the dynamic accessibility of a formal parameter
(all tagged and explicitly aliased parameters would have to carry accessibility
levels).
A formal
parameter of mode
in out or
out with discriminants is constrained
if either its nominal subtype or the actual parameter is constrained.
After
normal completion and leaving of a subprogram, for each
in out
or
out parameter that is passed by copy, the value of the formal
parameter is converted to the subtype of the variable given as the actual
parameter and assigned to it.
These conversions and
assignments occur in an arbitrary order.
Ramification: The conversions mentioned
above during parameter passing might raise Constraint_Error — (see
4.6).
Ramification: If any conversion or assignment
as part of parameter passing propagates an exception, the exception is
raised at the place of the subprogram call; that is, it cannot be handled
inside the
subprogram_body.
Proof: Since these checks happen before
or after executing the
subprogram_body,
the execution of the
subprogram_body
does not dynamically enclose them, so it can't handle the exceptions.
Discussion: The variable we're talking
about is the one denoted by the
variable_name
given as the
explicit_actual_parameter.
If this
variable_name
is a
type_conversion,
then the rules in
4.6 for assigning to a view
conversion apply. That is, if X is of subtype S1, and the actual is S2(X),
the above-mentioned conversion will convert to S2, and the one mentioned
in
4.6 will convert to S1.
Erroneous Execution
{
AI05-0008-1}
If the nominal subtype of a formal parameter with
discriminants is constrained or indefinite, and the parameter is passed
by reference, then the execution of the call is erroneous if the value
of any discriminant of the actual is changed while the formal parameter
exists (that is, before leaving the corresponding callable construct).
Implementation Permissions
{
AI12-0378-1}
If the actual parameter in a
parameter_association
with mode
out is a view conversion between two access types that
do not share a common ancestor type, the implementation may pass in the
null value of the type of the formal parameter instead of the value of
the actual parameter. It is implementation-defined under what circumstances
the implementation passes in the null value.
Implementation defined: The circumstances
in which the implementation passes in the null value for a view conversion
of an access type used as an out parameter.
Extensions to Ada 83
In Ada 95, a program can
rely on the fact that passing an object as an
out parameter does
not “de-initialize” any parts of the object whose subtypes
have implicit initial values. (This generalizes the RM83 rule that required
copy-in for parts that were discriminants or of an access type.)
Wording Changes from Ada 83
{
AI05-0299-1}
We have eliminated the subclause on Default Parameters, as it is subsumed
by earlier subclauses.
Inconsistencies With Ada 2005
{
AI05-0196-1}
Correction: Clarified that
out parameters
of an access type are not checked for null exclusions when they are passed
in (which is similar to the behavior for constraints). This was unspecified
in Ada 2005, so a program which depends on the behavior of an implementation
which does check the exclusion may malfunction. But a program depending
on an exception being raised is unlikely.
Incompatibilities With Ada 2005
{
AI05-0144-2}
Additional rules have been added to make illegal
passing the same elementary object to more than one
in out or
out parameters of the same call. In this case, the result in the
object could depend on the compiler version, optimization settings, and
potentially the phase of the moon, so this check will mostly reject programs
that are nonportable and could fail with any change. Even when the result
is expected to be the same in both parameters, the code is unnecessarily
tricky. Programs which fail this new check should be rare and are easily
fixed by adding a temporary object.
Wording Changes from Ada 2005
{
AI05-0008-1}
Correction: A missing rule was added to cover cases that were
missed in Ada 95 and Ada 2005; specifically, that an
in parameter
passed by reference might have its discriminants changed via another
path. Such cases are erroneous as requiring compilers to detect such
errors would be expensive, and requiring such cases to work would be
a major change of the user model (
in parameters with discriminants
could no longer be assumed constant). This is not an inconsistency, as
compilers are not required to change any current behavior.
{
AI05-0102-1}
Correction: Moved implicit conversion Legality Rule to
8.6.
{
AI05-0118-1}
Correction: Added a definition for positional parameters, as this
is missing from Ada 95 and later.
{
AI05-0142-4}
Rules have been added defining the legality and dynamic checks needed
for explicitly aliased parameters (see
6.1).
{
AI05-0144-2}
Additional rules have been added such that passing an object to an
in
out or
out parameter of a function is illegal if it is used
elsewhere in a construct which allows evaluation in an arbitrary order.
Such calls are not portable (since the results may depend on the evaluation
order), and the results could even vary because of optimization settings
and the like. Thus they've been banned.
Inconsistencies With Ada 2012
{
AI12-0378-1}
Correction: Added a permission to pass
null
so that value passed into an
out parameter for access types is
well-defined in the case of a view conversion.
Null may be passed
for any view conversion between unrelated access types; this is important
for conversions that may have problematic accessibility or tags. If the
permission is used and the
out parameter is read before it is
written (perhaps to read a bound or discriminant), Constraint_Error may
be raised by Ada 2022 when it would not have been in Ada 2012. Additionally,
if the called subprogram does not write the
out parameter at all,
the actual object will be overwritten with
null (and possibly
raise Constraint_Error if the object is null excluding), while the object
would be unchanged in Ada 2012. Such cases are thought to be rare, as
most
out parameters of access types are overwritten before being
read. In addition, at least one widely-used Ada compiler already passes
null in these cases.
Incompatibilities With Ada 2012
{
AI12-0074-1}
{
AI12-0159-1}
{
AI12-0377-1}
{
AI12-0378-1}
Corrigendum: Added rules to ensure that the
value passed into an
out parameter for scalar types is well-defined
in the case of a view conversion. The new rules can be incompatible.
View conversions from/to an unrelated type with the Default_Value aspect
specified are unlikely to occur in existing code, as the aspect is new
in Ada 2012. Declaring and passing a temporary rather than a view conversion
will eliminate the problem.
{
AI12-0095-1}
Corrigendum: Because of a rule added in
12.5.1,
the checks for the passing of an object to an explicitly aliased parameter
in a generic body were strengthened to use an assume the worst rule.
This case is rather unlikely as a formal private or derived type with
discriminants is required along with an explicitly aliased parameter
whose type doesn't statically match the formal type. Such a program is
very unlikely, especially as explicitly aliased parameters are a new
Ada 2012 feature.
Wording Changes from Ada 2012
{
AI12-0216-1}
Correction: The
in out parameter rule only applies to actual
parameters of elementary types. While this allows additional programs
(and thus could be considered an extension), it is unlikely to change
anything in a real program (it could only matter in a call with 4 or
more parameters, and then only if two composite parameters have matching
actuals). Thus we document it as a wording change.
{
AI12-0333-1}
Correction: Predicate checks are not made for inbound
out
parameters. The actual rule change that has this effect is found in
4.6
and is documented there.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe