Version 1.3 of ai05s/ai05-0083-1.txt
!standard 13.1(11/2) 08-07-10 AI05-0083-1/03
!standard 13.4(22/2)
!standard 13.3(40)
!class ramification 08-02-10
!status ARG Approved 7-0-1 08-06-21
!status work item 08-01-17
!status received 07-12-23
!priority Low
!difficulty Medium
!qualifier Clarification
!subject Representation values of formal parameters
!summary
Representation values such as 'Size and 'Alignment can have different values for
a formal parameter than for the associated actual parameter, even when the
parameter is passed by reference (and thus is a view of the object).
!question
Does 13.3(22/2) and 13.3(40/1) mean "view of an object" rather than just
"object"? (Yes.)
Generally, Section 13 text says "view" when it means it, and it usually does
not mean it. For instance, 13.1(11/2) says that all views of a type have the
same representation aspects.
The distinction between "view of an object" and just "object" matters when the
prefix of a representation attribute is a formal parameter. Consider:
type Lim is limited record
C : Character;
end record;
for Lim'Alignment use 2;
Lim_Obj : Lim;
for Lim_Obj'Alignment use 4;
procedure Do_Something (Param : in out Lim) is
begin
Put (Integer'Image(Param'Alignment));
end Do_Something;
Do_Something (Lim_Obj); --
Lim'Alignment is 2. Lim_Obj'Alignment is 4. Both of these values are required by
13.1(17) ("then that aspect of the entity is as specified"). But what is the
value of Param'Alignment? Param is passed by reference, so it is defined by 6.4.1(10)
to denote a view conversion of the actual parameter (Lim_Obj in this case).
If 13.3(22/2) means "object" (in the sense that every view has the same alignment),
then the value of Param'Alignment must be 4 (because it was specified for the
object), and this call must output "4".
If 13.3(22/2) means "view of an object", then the value of Param'Alignment doesn't
seem to be specified at all. It could be any value that is a factor of the object's
alignment, and it could even be larger than the object's alignment if, for
instance, the target aligns all objects to alignment 8.
These are very different answers, and the actual answer has impacts on
code generation.
Similar questions apply to Size and other attributes that can be specified for
objects.
!recommendation
(See Summary.)
!wording
Add an AARM Ramification after 13.1(11.c/2):
However, this does not apply to objects. Different views of an object can have
different representation aspects. For instance, an actual object passed
by reference and the associated formal parameter may have different values for
Alignment even though the formal parameter is merely a view of the
actual object. This is necessary to maintain the language design principle
that Alignments are always known at compile time.
!discussion
For alignment, the meaning of "object" (rather than "view of an object") is
bad because it requires the implementation to pass the alignment along with
the actual parameter if by-reference. (That wouldn't be necessary for a
by-copy parameter, which is a new object, but that just makes things
more confusing.) Moreover, it limits the reasoning that can be done
about the alignment of the passed object.
Luckily, AI05-0080-1 says that "view of" is almost always meant (implicitly)
in Static Semantic and Legality Rules. (And the "almost" was simply being
conservative; no one has an example of a rule for which it is not meant,
simply because such a rule would violate privacy.) As such, the language
clearly means that the alignment can be different for different views.
This even works for aspects of (sub)types, as all views of a type must have
the same aspect values (by 13.1(11/2)).
The Language Design Principle given in 13.3(21.d) provides more evidence
for this intepretation. It says that the alignment should always be
known at compile-time.
Thus we conclude that the language is clear. Param'Alignment in the example
of the question can be 2 (and be known at compile-time) even though
Lim_Obj'Alignment (the actual object) is 4.
!ACATS Test
In theory, no new ACATS tests are needed, although it might be worthwhile
to check the existing tests to verify whether Alignment is tested in this
way.
!appendix
From: Robert A. Duff
Sent: Sunday, December 23, 2007 6:58 PM
Here's a possible bug in the RM regarding alignment, found by Robert Dewar.
Suppose we have
type X is tagged null record;
procedure Munge (Arg : X);
type Y is new X;
for Y'Alignment use X'Alignment / 2;
So perhaps X'Alignment = 4 and Y'Alignment = 2.
Munge will assume its parameter is aligned to 4 bytes,
but Munge on a Y object might pass a misaligned object.
We don't see anything making this illegal, but it seems infeasible to implement
properly.
****************************************************************
From: Pascal Leroy
Sent: Monday, December 24, 2007 2:47 AM
> type X is tagged null record;
> procedure Munge (Arg : X);
> type Y is new X;
> for Y'Alignment use X'Alignment / 2;
How about 13.1(24/2):
"An implementation need not support a nonconfirming representation
item if it could cause an aliased object or an object of a
by-reference type to be allocated at a nonaddressable location or,
when the alignment attribute of the subtype of such an object is
nonzero, at an address that is not an integral multiple of that
alignment."
It seems to me that the alignment_clause would cause some objects of
type X (namely, those that are obtained by view-converting objects of
type Y) to be allocated at an address that is not a multiple of the
alignment of their subtype. Therefore, I believe that an
implementation is allowed to reject the alignment_clause. It
certainly was the intent of AI 291 to avoid this kind of pathology.
****************************************************************
From: Robert Dewar
Sent: Monday, December 24, 2007 4:47 AM
To me, leaving this implementation defined is wrong, since there is
no reasonable implementation model if you accept this alignment
clause.
The same is true for Size clauses if your implementation allows
Size clauses to change the internal representation.
e.g.
type x is tagged record
a,b,c,d,e,f,g,h : Boolean;
end record;
for X'Size use 72;
procedure Munge (Arg : X);
type xnew is new
for xnew'Size use 40;
I think it wrong for a compiler to be allowed to accept the above,
where the size clause causes implicit packing.
The whole business of Size and Alignment being subtype specific
is dubious in this context.
****************************************************************
From: Tucker Taft
Sent: Monday, December 24, 2007 12:04 PM
What about 13.3(32.1/2)? Doesn't this address this
issue exactly:
An implementation need not support an Alignment
specified for a derived tagged type which is
not a multiple of the Alignment of the parent
type. An implementation need not support a
nonconfirming Alignment specified for a derived
untagged by-reference type.
****************************************************************
From: Robert Dewar
Sent: Monday, December 24, 2007 1:10 PM
As per my note, I don't think IA is good enough here
Making something legal for which we have no reasonnable
implentation model, then saying that you can but do not
have to reject it is bad language design policy in
my view.
****************************************************************
From: Robert A. Duff
Sent: Monday, December 24, 2007 1:42 PM
I agree, but this style is used throughout chapter 13.
At this point, I'd say "not broken enough to fix".
****************************************************************
From: Robert Dewar
Sent: Monday, December 24, 2007 7:25 AM
Is that really true, I don't see other examples like
this, can you quote an example of what you mean by
throughout. To me all the other limitations you
are allowed are about things that might be
implementable on some architectures but not
on others.
Alignment is a bit of an orphan anyway, for instance
we remembered to say:
> 53 A Size clause on a composite subtype should not affect the internal
> layout of components.
But we forgot to say the same for alignment, and of
course it is just as important.
By the way, I just noticed the following note:
> 37 (4) The Alignment of a composite object is always equal to the least
> common multiple of the Alignments of its components, or a multiple
> thereof.
rubbish of course: [For the record, this note was deleted by the Amendment;
it's not present in the current Ada standard - ED.]
type x is record
a,b,c : integer;
end record;
for x'alignment use 1;
though come to think of it, what on earth does the alignment of a
component mean? I took it to mean the alignment of the type of the
component? Could it mean something else.
****************************************************************
From: Tucker Taft
Sent: Monday, December 24, 2007 6:36 PM
This is implementation device under the special
heading of "recommended level of support" so
most implementors treat is as gospel, since
they all support the systems-programming annex.
In general, we allow implementations to accept
almost any representation item if they really
want to, but most follow the "recommended level
of support" very closely, and certainly users
who go beyond that are clearly venturing into
implementation-dependent territory.
****************************************************************
From: Randy Brukardt
Sent: Wednesday, December 26, 2007 8:42 PM
> To me, leaving this implementation defined is wrong, since there is
> no reasonable implementation model if you accept this alignment
> clause.
I strongly disagree with this statement: it makes perfect sense to accept
this alignment clause in some implementation models on some targets.
For instance, Janus/Ada on the x86 does not use any instructions whose
correctness depend on the alignment of an object. For this, procedure Munge
will work with an object of any (non-zero) alignment. In such a case, there
might be a performance difference, but generally it is a mistake for the
compiler to second-guess what the customer wants. At most it would make
sense to issue a warning on the alignment clause: "This alignment may lead
to reduced performance in inherited subprograms."
Of course, such an alignment clause isn't portable, and I have no problem
imagining architectures where implementing it would be difficult or
impossible. Such architectures have a number of reasons that they can use to
reject the alignment clause (such as the rules noted by others). It might be
useful for Janus/Ada to note that with another warning: "This alignment
clause may not be supported on other Ada compilers." -- but again that is no
reason to disallow it.
There doesn't seem to be any good reason to disallow representation clauses
that may work on some implementations (these clauses are already
target-dependent, so portability is not a major issue beyond ensuring a
basic level of support). The most important thing is to ensure that
implementations aren't *required* to do the impossible, and that is already
done.
As such, not only don't I think that a rule change is needed, but I think
that any such change would be actively harmful.
****************************************************************
From: Robert Dewar
Sent: Wednesday, December 26, 2007 9:01 PM
> For instance, Janus/Ada on the x86 does not use any instructions whose
> correctness depend on the alignment of an object. For this, procedure Munge
> will work with an object of any (non-zero) alignment. In such a case, there
> might be a performance difference, but generally it is a mistake for the
> compiler to second-guess what the customer wants. At most it would make
> sense to issue a warning on the alignment clause: "This alignment may lead
> to reduced performance in inherited subprograms."
It will work, but the resulting program is erroneous, because you have
an object which does not match the alignment of the type. That's a clear
error to me.
> Of course, such an alignment clause isn't portable, and I have no problem
> imagining architectures where implementing it would be difficult or
> impossible. Such architectures have a number of reasons that they can use to
> reject the alignment clause (such as the rules noted by others). It might be
> useful for Janus/Ada to note that with another warning: "This alignment
> clause may not be supported on other Ada compilers." -- but again that is no
> reason to disallow it.
>
> There doesn't seem to be any good reason to disallow representation clauses
> that may work on some implementations (these clauses are already
> target-dependent, so portability is not a major issue beyond ensuring a
> basic level of support). The most important thing is to ensure that
> implementations aren't *required* to do the impossible, and that is already
> done.
"may work" is fine, but "happen to work even if the program is
erroneous" is quite another matter
> As such, not only don't I think that a rule change is needed, but I think
> that any such change would be actively harmful.
I do not think it is harmful to stop programmers writing programs
that are erroneous.
Anyway, no big deal, if people want to write erroneous Janus programs
that's OK with me, but I think in fact that all compilers should prevent
this if they do not have an implementation model that guarantees that
objects have the alignment specified by their types. Basically Janus
is simply not following the alignment specified for the parent type
in this situation, it may get away with this non-conformance, but
for example, an ACATS test that verified that the alignment was
correct in this case is not only a valid test, but perhaps even a
desirable one if there are implementations trying to get away with
not obeying alignment clauses in this situation.
****************************************************************
From: Robert Dewar
Sent: Wednesday, December 26, 2007 9:11 PM
To say a little more about Randy and the Janus approach
here, since there is a general principle involved.
It is fine to have compilers that accept rep clauses
that other compilers reject PROVIDING that they
respect these rep clauses.
In the situation
type R is ... some by-reference type ...
for R'Alignment use 4;
that should mean, if accepted that all objects of
this type are aligned on a boundary of 4. If your
implementation cannot guarantee this, it must reject
this clause.
Now if we accept
type R1 is new R;
for R1'Alignment use 2;
And the implementation mechanism is to call routines
defined for R passing objects of type R with an
alignment of 2 and not 4, then to me that's plain
wrong, since it is violating the rep clause on R.
It's fine to accept both clauses ONLY if you can
obey both, and I still see no implementation model
that can achieve this, Randy's report of a
non-conforming model that happens to work most
of the time is not a counter-example.
For example, if I am in the routine involved, I
am quite entitled to assume that Arg'Address is
a multiple of 4, and base some assumptions on this.
If that's not true, then the compiler is wrong.
I said in my previous code that the code is
erroneous, that's not really right, the code
is fine, what is wrong is for any compiler to
accept it!
****************************************************************
From: Randy Brukardt
Sent: Wednesday, December 26, 2007 10:40 PM
> It will work, but the resulting program is erroneous, because you have
> an object which does not match the alignment of the type. That's a clear
> error to me.
To be pendantic, I don't think you mean "erroneous", because what the
program does is well-defined. It surely isn't going to "format the hard
disk", as erroneous programs can do. We don't have a formal word for this
sort of issue and you need to avoid using a formally defined term else your
meaning be mis-interpreted. End pendantic.
More below.
> "may work" is fine, but "happen to work even if the program is
> erroneous" is quite another matter
The program is not "erroneous". Whether it is even wrong is not clear to me:
does the alignment of a formal parameter reflect that of the actual, or is
it just a nominal alignment? In the former case, you'd have to pass the
alignment value with all objects -- which seems to be a silly thing to do.
Or you've have to ban all non-confirming alignment clauses on derived types
that *could* be passed by reference. To do that latter in the language would
require banning all non-confirming alignment clauses on derived composite
types. And you'd have to do the same for size clauses. That seems pretty
draconian to me.
> > As such, not only don't I think that a rule change is needed, but I think
> > that any such change would be actively harmful.
>
> I do not think it is harmful to stop programmers writing programs
> that are erroneous.
"Wrong", perhaps, but not erroneous.
> Anyway, no big deal, if people want to write erroneous Janus programs
> that's OK with me, but I think in fact that all compilers should prevent
> this if they do not have an implementation model that guarantees that
> objects have the alignment specified by their types. Basically Janus
> is simply not following the alignment specified for the parent type
> in this situation, it may get away with this non-conformance, but
> for example, an ACATS test that verified that the alignment was
> correct in this case is not only a valid test, but perhaps even a
> desirable one if there are implementations trying to get away with
> not obeying alignment clauses in this situation.
My view has always been that the only place that size and alignment really
matter is on the declaration of [original] objects (and components). It's
surely important for atomic and volatile objects; it's less important for
"regular" objects, and it is not important at all for non-original views of
"original" objects.
For a formal parameter, you can't write non-pathological Ada code that
depends on the alignment (assuming an implementation model where there
aren't any alignment-specific instructions used): the only such code that
you can write is to test the 'Address value, and that is not a useful thing
to do. So I don't see any reason for a routine to *assume* the alignment
value of its formal parameter. Indeed, anything else would be too expensive
inside of a code-shared generic -- Janus/Ada only ensures that the size and
alignment match for "original" objects, and not for views of such objects.
(We used to go even further in generics, but we had to change it in order to
support aliased objects inside of generics to be accessed by types outside
of the generic.)
Perhaps I'm just seriously confused (not unlikely in the case of 'Size and
'Alignment - most of what I read about them appear to be nonsense). In any
case, we discussed whether to try to craft rules to make the
impossible-to-implement cases illegal when we were discussing the issues
with limited by-reference types, and I'm pretty sure we decided that
crafting such rules would be very hard since either they'd have to be very
incomplete or they would go too far (as the one I suggested above). If they
are left incomplete, then we'd need both the existing permissions and the
extra rules making some cases illegal, which seems like saying the same
thing twice (and in different ways) -- which has proven to be a maintenance
hazard in the past.
So the decision was made to let implementers "do the right thing". It is
apparent that it is not clear (to at least this implementer) what the "right
thing" is, so perhaps there would be some benefit to revisiting that, but I
don't think that there is likely to be any interest in making more things
illegal.
****************************************************************
From: Robert Dewar
Sent: Wednesday, December 26, 2007 10:56 PM
> My view has always been that the only place that size and alignment really
> matter is on the declaration of [original] objects (and components). It's
> surely important for atomic and volatile objects; it's less important for
> "regular" objects, and it is not important at all for non-original views of
> "original" objects.
Well that might be your view, but that is not what the RM says, it says
> 26.3/2 For an object X of subtype S, if S'Alignment is not zero, then
> X'Alignment is a nonzero integral multiple of S'Alignment unless
> specified otherwise by a representation item.
it makes no distinction between regular and original objects whatever
the heck that means, it just says that all objects have this alignment,
The subprogram parameter is an object, it clearly is covered by this
rule. I don't know how you conclude otherwise.
> For a formal parameter, you can't write non-pathological Ada code that
> depends on the alignment (assuming an implementation model where there
> aren't any alignment-specific instructions used): the only such code that
> you can write is to test the 'Address value, and that is not a useful thing
> to do.
I see, so now we are down to saying that it's OK to be non-conforming
because Randy declares that the non-conformance is a pathology :-)
Here is non-pathological use of 'Address, you take addresses of things
that you know are aligned to 4 bytes, convert them to Integer'Address
values, then use the low two bits for storing bits of some kind. Odd
code, and probably too clever, but certainly valid, and by no stretch
of the imagination invalid.
These addresses might for example be used in some kind of caching
scheme to recognize parameters that were previously passed.
****************************************************************
From: Randy Brukardt
Sent: Wednesday, December 26, 2007 11:42 PM
> Well that might be your view, but that is not what the RM says, it says
>
> > 26.3/2 For an object X of subtype S, if S'Alignment is not zero, then
> > X'Alignment is a nonzero integral multiple of S'Alignment unless
> > specified otherwise by a representation item.
>
> it makes no distinction between regular and original objects whatever
> the heck that means, it just says that all objects have this alignment,
> The subprogram parameter is an object, it clearly is covered by this
> rule. I don't know how you conclude otherwise.
This wording says "object" rather than "view of an object". A
pass-by-reference formal parameter is a "view of an object" - it's not
really an object itself. (The wording of the Standard seems confused on this
point, but it hardly can be otherwise.)
That means that the value of Arg'Alignment has to be that of the actual.
Consider an inversion of your original example:
type X is tagged null record;
procedure Munge (Arg : X);
type Y is new X;
for Y'Alignment use X'Alignment * 2
This seems useful, but (in the absence of expensive passing of the
alignment), Arg will have the wrong alignment inside of Munge. The "error"
will be harmless (any value passed will have at least the proper alignment),
but it still is wrong.
Following your suggestion for a language legality rule, you'd need a rule
similar to 13.1(10): "For a composite derived type, no subtype-related
representation items are allowed if the parent type has any user-defined
primitive subprograms." But that seems like too much a limitation to me.
Now, you can argue that the original wording means "view of an object". But
if you do that, then you could have different values for different views of
an object (such as a partial and full view) -- and that violates 13.1(11/2).
And surely the formal parameter passed by-reference is a view of the actual
object (6.4.1(10) says that the formal denotes a view conversion of the
actual) - and thus the formal and actual have to have the same alignment by
13.1(11/2). So this line of argument leads nowhere.
It does seem that something is wrong somewhere with the wording for Size and
Alignment, but it surely isn't clear to me what it is.
> > For a formal parameter, you can't write non-pathological Ada code that
> > depends on the alignment (assuming an implementation model where there
> > aren't any alignment-specific instructions used): the only such code that
> > you can write is to test the 'Address value, and that is not a useful thing
> > to do.
>
> I see, so now we are down to saying that it's OK to be non-conforming
> because Randy declares that the non-conformance is a pathology :-)
I don't think arguing from pathology is very strong (there is reasonable
disagreement on what is a pathology and what is just clever code), but I
don't think it is worth deciding rules that can't matter in actual practice.
We'd got more interesting things to do.
> Here is non-pathological use of 'Address, you take addresses of things
> that you know are aligned to 4 bytes, convert them to Integer'Address
> values, then use the low two bits for storing bits of some kind. Odd
> code, and probably too clever, but certainly valid, and by no stretch
> of the imagination invalid.
>
> These addresses might for example be used in some kind of caching
> scheme to recognize parameters that were previously passed.
Well, I don't find this example too convincing -- it's too tricky by half.
My rule of thumb for deciding on whether something is a pathology is whether
I could convince a customer that it wasn't legitimate should the need arise.
But I realize your mileage may vary, and I'll be happy to agree to disagree
on this point. It isn't critical anyway -- the real question is whether you
think Size and Alignment should be specifiable at all on derived composite
types.
****************************************************************
From: Robert Dewar
Sent: Wednesday, December 26, 2007 9:01 PM
> That means that the value of Arg'Alignment has to be that of the actual.
> Consider an inversion of your original example:
>
> type X is tagged null record;
> procedure Munge (Arg : X);
> type Y is new X;
> for Y'Alignment use X'Alignment * 2
>
> This seems useful, but (in the absence of expensive passing of the
> alignment), Arg will have the wrong alignment inside of Munge. The "error"
> will be harmless (any value passed will have at least the proper alignment),
> but it still is wrong.
No, it's not wrong, any object of type Y has an alignment of
X'Alignment. That's how alignments work!
> Well, I don't find this example too convincing -- it's too tricky by half.
> My rule of thumb for deciding on whether something is a pathology is whether
> I could convince a customer that it wasn't legitimate should the need arise.
tricky /= pathological. I have seen this technique of using the unset
bits of aligned pointers for flags used many times, e.g. in the heap
manager for the old Alsys x86 compiler.
> But I realize your mileage may vary, and I'll be happy to agree to disagree
> on this point. It isn't critical anyway -- the real question is whether you
> think Size and Alignment should be specifiable at all on derived composite
> types.
Well the business of changing internal layout is separate of course. As
to whether you should allow size and alignment to be specified for
derived composite types, yes, of course you want that. In particular,
it is routine for the alignment of a derived type to be larger than
that of the base type, as in:
type X is tagged record
Y : Integer;
end record;
for X'Alignment use 4; -- confirming rep clause
type X1 is X with record
Z : Long_Float;
end record;
for X1'Alignment use 8; -- confirming rep clause
Just as the size can get bigger with no ill effects, the
alignment can get bigger with no ill effects, since you
can't convert an X to an X1, and when you convert an X1
to an X, the view has the right size and alignment.
It's never a problem if the alignment is too big. An alignment
of 4 means the lower bits are xxxxxx00, it does not mean they
are xxxxx100!
The issue is can you specify a smaller size or alignment for
a derived type, well the answer for size is obviously not I
would say, though we would get a similar situation with
type X is tagged record
Y : Integer;
end record;
for X'Size use 1600; -- lots of space to spare
type X1 is X with record
Z : Long_Float;
end record;
for X1'Size use 1200; -- not so much space to spare
And I think the situation is the same with an alignment.
Here is something that I think should be perfectly fine in
any subprogram:
type Arg is .. some by ref type ..
for Arg'Alignment use 4;
procedure X (A : Arg) is -- Arg has alignment of 4
R : Integer;
for R'Address use A'Address;
Now according to your formulation, the integer could end up
with a wrong alignment, which seems clearly wrong to me. R
is a regular (or whatever adjective you came up with) object
here, and it is explicitly erroneous to specify an address for
an object that is not sufficiently aligned.
Are you really saying you think this last example is
potentially erroneous becuase a view can be underaligned?
That sounds very wrong to me.
I really don't think alignments have been thought through
carefully enough. You have a rather casual view, because
you are on a machine where most (not all) hardware types
work OK (though with huge time penalties) if they are
unaligned, but most architectures are not like that.
****************************************************************
From: Randy Brukardt
Sent: Thursday, December 27, 2007 5:35 PM
> > This seems useful, but (in the absence of expensive passing of the
> > alignment), Arg will have the wrong alignment inside of Munge. The "error"
> > will be harmless (any value passed will have at least the proper alignment),
> > but it still is wrong.
>
> No, it's not wrong, any object of type Y has an alignment of
> X'Alignment. That's how alignments work!
I fear you've missed my point. I'm explaining how the wording in the
Standard defines alignments. That isn't making any value judgment on whether
that is good or bad, and surely isn't describing how I think alignments
should work. Don't shoot the messenger!
For the record, I think I agree with you that something is wrong here. But
that doesn't change what the standard says.
...
> > ... It isn't critical anyway -- the real question is whether you
> > think Size and Alignment should be specifiable at all on
> > derived composite types.
>
> Well the business of changing internal layout is separate of course. As
> to whether you should allow size and alignment to be specified for
> derived composite types, yes, of course you want that.
Good. Then I think we agree on the basic point that we shouldn't disallow
anything that is possibly useful.
...
> The issue is can you specify a smaller size or alignment for
> a derived type, well the answer for size is obviously not I
> would say, though we would get a similar situation with
>
> type X is tagged record
> Y : Integer;
> end record;
> for X'Size use 1600; -- lots of space to spare
>
> type X1 is X with record
> Z : Long_Float;
> end record;
> for X1'Size use 1200; -- not so much space to spare
>
> And I think the situation is the same with an alignment.
I think that the ARG intended for implementers to "do the right thing" here.
But as our discussion has pointed out, it isn't necessarily obvious what the
right thing is. At the very least, the AARM should be more explicit.
> Here is something that I think should be perfectly fine in
> any subprogram:
>
> type Arg is .. some by ref type ..
> for Arg'Alignment use 4;
>
> procedure X (A : Arg) is -- Arg has alignment of 4
> R : Integer;
> for R'Address use A'Address;
>
> Now according to your formulation, the integer could end up
> with a wrong alignment, which seems clearly wrong to me. R
> is a regular (or whatever adjective you came up with) object
> here, and it is explicitly erroneous to specify an address for
> an object that is not sufficiently aligned.
>
> Are you really saying you think this last example is
> potentially erroneous because a view can be underaligned?
> That sounds very wrong to me.
Well, in our compiler, Integer (and all predefined types) have an alignment
of 1, so there is no problem. Only alignments that are explicitly specified
have a non-zero value. (There is a second layer of "guideline" alignments
that are enforced only when convenient, which is used to try to align things
like integer objects. That mess came about because we took Ada 95's 13.3(26)
seriously, and do not allow any representation items to specify (explicitly
or implicitly) a smaller alignment. That rule has been deleted and generally
was decided to be wrong, so I could try to simplify our implementation, but
that would be extra work for something that isn't broken.)
In any case, all uses of 'Address are potentially erroneous (by 13.3(13))
and not portable. This particular example surely isn't portable, as
A'Address might very well point at a tag or descriptor. So the only way to
figure out whether an 'Address clause will work is to read your
documentation or talk to your vendor. And not to expect it to work somewhere
else.
> I really don't think alignments have been thought through
> carefully enough. You have a rather casual view, because
> you are on a machine where most (not all) hardware types
> work OK (though with huge time penalties) if they are
> unaligned, but most architectures are not like that.
I plead guilty to working on machines where alignment issues aren't
critical. I'm dubious about the "huge time penalties" (on most code I've
worked on, alignment hasn't made a noticeable difference, and testing has
shown that code on which it matters is rare), but surely I've tried to make
it possible to align things in those cases where it matters. But every
target that I've worked on, it would *work* even though it wasn't optimal.
That even included the U2200, which reminds me of a funny story tangentially
related to this discussion. On the U2200, we targeted a Unisys-provided
backend that took care of code generation and included support for unaligned
objects (because it had originally been written for a bad C front-end that
couldn't align anything). Quite a while after I finished the implementation
of alignments in our front-end, the guy on our team responsible for
machine-level debugging off-handedly remarked that it was hard to debug the
code because everything was unaligned. This was a big surprise to me, since
I'd spent more than a month ensuring that every place memory was allocated
called the appropriate alignment code. After some code inspection, I found
an off-by-one error in my alignment code: the effect was that every address
ended with "11" rather than "00"! Oops! Fixing that eliminated that
particular gripe, and presumably made the programs run faster.
Moral: even when I've tried to do the right thing for alignment, I've gotten
it wrong.
But this isn't about me. It's about the wording of the Standard. And, in
particular, whether different views of an object can have different
representation values. And, if so, what are they?
My interpretation has been that 'Size and 'Alignment work the same way, and
that is backed up by the wording of their definitions. Moreover, I've always
assumed that if 'Size or 'Alignment is specified for an object, that you
*must* get that value if you query it. That means that neither 'Size nor
'Alignment can be known at compile-time for formal parameters passed by
reference, because the attribute has to return the value (possibly
specified) for the original object. (They're never static in such a case, so
there is no legality depending on this; they can only be queried.)
That makes sense for 'Size, since it can be valuable to know the actual
size, and anything else is going to be returning gibberish:
type Arr is array (Positive range <>) of Character;
procedure Check (Arg : Arr; Sz : Natural);
Obj4 : Arr(1..4);
for Obj4 use 32;
Obj8 : Arr(1..8);
for Obj8 use 64;
procedure Check (Arg : Arr; Sz : Natural) is
begin
if Arg'Size /= Sz then
Put_Line ("** Not size of actual");
end if;
end Check;
Check(Obj4, Obj4'Size);
Check(Obj8, Obj8'Size);
On the other hand, doing this for 'Alignment violates Dewar's rule, because
it essentially says that the alignment of a reference parameter is unknown.
But the primary purpose of alignment is to allow the compiler to generate
code only for aligned references, and clearly the alignment has to be known
at compile-time for that to work.
So I think I agree that there is something wrong here. But I think Robert
has convinced me that I'm not the person to try to fix it. Hopefully someone
else will take a crack at it.
****************************************************************
Questions? Ask the ACAA Technical Agent