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
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
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
Name Resolution Rules
To be honest:
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
of the call.
If the mode is in
, the actual is interpreted
as an expression
otherwise, the actual is interpreted only as a name
This formally resolves
the ambiguity present in the syntax rule for explicit_actual_parameter
Note that we don't actually require that the actual be a name
if the mode is not in
; we do that below.
If the mode is in out
, the actual
shall be a name
that denotes a variable.
requirement that the actual be a (variable) name
is not an overload resolution rule, since we don't want the difference
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.
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
type of the actual parameter associated with an access parameter shall
be convertible (see 4.6) to its anonymous access
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.
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
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
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).
accessibility check (and its dynamic cousin as well) can only fail if
the function call is used to directly initialize a built-in-place object
with a master different than that enclosing the call. The only place
all of those conditions exist is in the initializer of an allocator;
in all other cases this check will always pass.
statically denote the same stand-alone object or parameter; or
are dereferences (implicit or explicit) and the dereferenced names
are known to denote the same object; or
are known to denote the same object, and each of the pairs of corresponding
index values are either bodt static expressions with the same value or
that are known to denote the same object; or
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.
This exposes known renamings of slices, indexing, and so on to this
definition. In particular, if we have
C : Character renames S(1);
C and S(1) are known to denote the same object.
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;
Ptr := new Some_Type'(...);
P (Func_With_Out_Params (Ptr.all), 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).
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);
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.
or not names
are known to denote the same object is determined statically. If the
name contains some dynamic portion other than a dereference, indexed_component,
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.
"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
S : String(1..10);
ONE : constant Natural := 1;
R : Character renames S(1);
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.)
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:
For each name
N 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
If a construct C has two or more direct
constituents that are names
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:
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
(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
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
Reason: These rules
prevent obvious cases of dependence on the order of evaluation of names
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.
do not check expressions that are evaluated only because of a component
initialized by default in an aggregate (via <>).
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,
For an access type, the formal parameter is initialized from the value
of the actual, without checking that the value
satisfies any constraint, any predicate, or any exclusion of the null
value a constraint check
Reason: This preserves the Language Design
Principle that an object of an access type is always initialized with
a “reasonable” value.
For a scalar type that has the Default_Value aspect
specified, the formal parameter is initialized from the value of the
actual, without checking that the value satisfies any constraint or any
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.
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).
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.
Reason: This ensures that no part of
an object of such a type can become “de-initialized” by being
part of an out parameter.
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.
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 might 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.
This case covers scalar types that do not have
, 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.
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
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).
parameter of mode in out
with discriminants is constrained
if either its nominal subtype or the actual parameter is constrained.
normal completion and leaving of a subprogram, for each in 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.
The conversions mentioned
above during parameter passing might raise Constraint_Error — (see
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
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.
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
will convert to S1.
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
Extensions to Ada 83
In Ada 95, a program can
rely on the fact that passing an object as an out
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
We have eliminated the subclause on Default
Parameters, as it is subsumed by earlier clauses and subclauses.
Inconsistencies With Ada 2005
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
Incompatibilities With Ada 2005
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 non-portable 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
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
Correction: Moved implicit conversion Legality
Rule to 8.6.
Correction: Added a definition for positional
parameters, as this is missing from Ada 95 and later.
Rules have been added defining the legality and
dynamic checks needed for explicitly aliased parameters (see 6.1).
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.
Ada 2005 and 2012 Editions sponsored in part by Ada-Europe