6.14 Address Clauses

The reference manual allows a general restriction on representation clauses,
as found in RM 13.1(22):

An implementation need not support representation
items containing nonstatic expressions, except that
an implementation should support a representation item
for a given entity if each nonstatic expression in the
representation item is a name that statically denotes
a constant declared before the entity.

In practice this is applicable only to address clauses, since this is the
only case in which a non-static expression is permitted by the syntax. As
the AARM notes in sections 13.1 (22.a-22.h):

22.a Reason: This is to avoid the following sort of thing:
22.b X : Integer := F(...);
Y : Address := G(...);
for X'Address use Y;
22.c In the above, we have to evaluate the
initialization expression for X before we
know where to put the result. This seems
like an unreasonable implementation burden.
22.d The above code should instead be written
like this:
22.e Y : constant Address := G(...);
X : Integer := F(...);
for X'Address use Y;
22.f This allows the expression “Y” to be safely
evaluated before X is created.
22.g The constant could be a formal parameter of mode in.
22.h An implementation can support other nonstatic
expressions if it wants to. Expressions of type
Address are hardly ever static, but their value
might be known at compile time anyway in many
cases.

GNAT does indeed permit many additional cases of non-static expressions. In
particular, if the type involved is elementary there are no restrictions
(since in this case, holding a temporary copy of the initialization value,
if one is present, is inexpensive). In addition, if there is no implicit or
explicit initialization, then there are no restrictions. GNAT will reject
only the case where all three of these conditions hold:

The type of the item is non-elementary (e.g. a record or array).

There is explicit or implicit initialization required for the object.
Note that access values are always implicitly initialized, and also
in GNAT, certain bit-packed arrays (those having a dynamic length or
a length greater than 64) will also be implicitly initialized to zero.

The address value is non-static. Here GNAT is more permissive than the
RM, and allows the address value to be the address of a previously declared
stand-alone variable, as long as it does not itself have an address clause.

However, the prefix of the address clause cannot be an array component, or
a component of a discriminated record.

As noted above in section 22.h, address values are typically non-static. In
particular the To_Address function, even if applied to a literal value, is
a non-static function call. To avoid this minor annoyance, GNAT provides
the implementation defined attribute 'To_Address. The following two
expressions have identical values:

To_Address (16#1234_0000#)
System'To_Address (16#1234_0000#);

except that the second form is considered to be a static expression, and
thus when used as an address clause value is always permitted.

Additionally, GNAT treats as static an address clause that is an
unchecked_conversion of a static integer value. This simplifies the porting
of legacy code, and provides a portable equivalent to the GNAT attribute
To_Address.

Another issue with address clauses is the interaction with alignment
requirements. When an address clause is given for an object, the address
value must be consistent with the alignment of the object (which is usually
the same as the alignment of the type of the object). If an address clause
is given that specifies an inappropriately aligned address value, then the
program execution is erroneous.

Since this source of erroneous behavior can have unfortunate effects, GNAT
checks (at compile time if possible, generating a warning, or at execution
time with a run-time check) that the alignment is appropriate. If the
run-time check fails, then Program_Error is raised. This run-time
check is suppressed if range checks are suppressed, or if the special GNAT
check Alignment_Check is suppressed, or if
pragma Restrictions (No_Elaboration_Code) is in effect.

Finally, GNAT does not permit overlaying of objects of controlled types or
composite types containing a controlled component. In most cases, the compiler
can detect an attempt at such overlays and will generate a warning at compile
time and a Program_Error exception at run time.

An address clause cannot be given for an exported object. More
understandably the real restriction is that objects with an address
clause cannot be exported. This is because such variables are not
defined by the Ada program, so there is no external object to export.

It is permissible to give an address clause and a pragma Import for the
same object. In this case, the variable is not really defined by the
Ada program, so there is no external symbol to be linked. The link name
and the external name are ignored in this case. The reason that we allow this
combination is that it provides a useful idiom to avoid unwanted
initializations on objects with address clauses.

When an address clause is given for an object that has implicit or
explicit initialization, then by default initialization takes place. This
means that the effect of the object declaration is to overwrite the
memory at the specified address. This is almost always not what the
programmer wants, so GNAT will output a warning:

As indicated by the warning message, the solution is to use a (dummy) pragma
Import to suppress this initialization. The pragma tell the compiler that the
object is declared and initialized elsewhere. The following package compiles
without warnings (and the initialization is suppressed):

In both of these cases, A
and B become aliased to one another via the
address clause. This use of address clauses to overlay
variables, achieving an effect similar to unchecked
conversion was erroneous in Ada 83, but in Ada 95 and Ada 2005
the effect is implementation defined. Furthermore, the
Ada RM specifically recommends that in a situation
like this, B should be subject to the following
implementation advice (RM 13.3(19)):

19 If the Address of an object is specified, or it is imported
or exported, then the implementation should not perform
optimizations based on assumptions of no aliases.

GNAT follows this recommendation, and goes further by also applying
this recommendation to the overlaid variable (A
in the above example) in this case. This means that the overlay
works "as expected", in that a modification to one of the variables
will affect the value of the other.