Version 1.3 of ai05s/ai05-0123-1.txt
!standard 4.5.2(10-26) 08-11-17 AI05-0123-1/02
!class Amendment 08-10-17
!status work item 08-10-17
!status received 08-09-03
!priority Low
!difficulty Hard
!subject Composibility of equality
!summary
!summary
Primitive "=" ops for tagged and untagged records compose alike .
!problem
A User-defined primitive "=" op for a tagged type "composes" in
the sense that the predefined equality operator for an
enclosing type which has one or more components of the given tagged
type is defined in terms of the user-defined "=" op; the overridden
predefined "=" op for the tagged type does not reemerge in this case.
This is not true for untagged types.
Tagged types were new in Ada95; thus the introduction of this rule
in Ada95 was not an incompatible change. This rule was not introduced
for untagged types because that would have been an incompatible
change.
It has since been decided that, at least for untagged records,
this was probably a mistake. Typically the feared "incompatible
change" results in the correction of a bug.
!proposal
If a nonlimited composite type contains a component
of an untagged record type with a user-defined primitive
"=" op, then the predefined equality op for the composite type
is defined in terms of the user-defined equality op of the
record type in the same way as if the record type were tagged.
The situation is less clear for arrays because of the
predefined lexicographic ordering operators for discrete array types
(a change to "=" without a corresponding change to, say, "<=" seems
like a bad idea). Thus, no analogous changes are proposed for
array types.
Tuck observes that "it would be pretty weird if the directly called
equality didn't agree with the composed equality". For example, a
call to the "=" operator for an untagged record type ought
to yield the same answer aa a corresponding call to the the predefined
"=" operator for a one-field record type whose one component is of
the first record type.
It is not clear how this principle applies in a some corner cases:
1) An untagged record type with a user-defined abstract
primitive "=" op.
2) An untagged record type declared in the visible part of a package
with a user-defined primitive "=" op declared in the private
part or the body of the package.
3) An untagged record type with a user-defined primitive "=" op
where the predefined "=" op is referenced before it is
overridden.
Tuck suggests a general principle that 'you would either get a
compile-time error, or always get the same semantics as Ada 2005
for a direct call on "=" '.
With this in mind, the preceding corner cases are all
defined to be statically illegal.
In the case of a generic formal private type where the corresponding
actual parameter is an untagged record type with a user-defined
primitive "=" op, reemergence does not take place. A call to the
"=" operator associated with the formal type results in a call to
the user-defined "=" op.
!wording
** TBD **
!discussion
** TBD **
!example
** TBD **
!ACATS test
** TBD **
!appendix
From: John Barnes
Sent: Wednesday, September 3, 2008 1:47 AM
I have a request from members of the BSI Ada panel to consider the attached
thoughts on composability of equality.
Can we put it on the agenda for the next meeting please?
!standard 08-09-03 AI05-nnnnn-1/01
!class Amendment
!status work item
!status received
!priority Medium
!difficulty Easy
!subject Composability of equality
!summary
A pragma Composable_Equality is proposed to indicate that equality for the type
is to be composable.
!problem
Consider the following example
type My_Float is new Float;
function "=" (L, R : My_Float) return Boolean is
begin
if L > 0.99 * R and then L < 1.01 * R then
return True; -- Near enough
else
return False;
end if;
end "=";
type Position is
record
Latitude : My_Float;
Longitude : My_Float;
end record;
Position1 : Position := (90.00, 90.00);
Position2 : Position := (90.01, 89.99);
begin
if Position1 = Position2 then –- Uses original predefined equals
-- for components
The user might expect that the type Position would use the newly defined
equality for its components. However, predefined equality reemerges unless
the type is tagged. The user therefore has to remember to redefine equality
for the type Position as well. This is a potential source of errors and
confusion.
!proposal
Introduce a pragma Composable_Equality(Type_Name) to indicate that the
same composability rules that apply to tagged types also apply to the type
Type_Name.
!wording
tbd
!discussion
The fact that all types do not compose for equality is surprising.
However, although it was changed for tagged types when they were introduced
in Ada 95, it was not done for nontagged types for reasons of backward
compatibility.
One possible workaround is to declare the types to be tagged but this is
not always appropriate.
Are there similar problems with user-defined operations in generics
and what should be done about it?
!example
!ACATS test
!appendix
****************************************************************
From: Robert Dewar
Sent: Wednesday, September 3, 2008 9:09 AM
Can we have a reminder of the rationale behind NOT making this
commposable in the first place?
****************************************************************
From: Robert Dewar
Sent: Wednesday, September 3, 2008 9:09 AM
I would also allow this as a configuration pragma without a type name,
meaning that it applies to all types (really quite a language change,
but that's really the intent here).
****************************************************************
From: Pascal Leroy
Sent: Friday, September 5, 2008 12:36 AM
I find the idea of so-called composable equality for elementary types
(especially for floats, as shown in the example) extremely dangerous.
There is no way that the behaviour of any generic (notably the ones
in Ada.Numerics) can be predicted. I am pretty sure that the IBM
implementation of the elementary functions and the matrix algorithms
would fall over dead with the "=" shown in the example (or at least
return random results -- think of the computation of eigenvalues as
an example). Furthermore, many of these generics expect some
consistency between "=" and the other relational operators.
Presumably redefining "=" doesn't affect "<=", which is unlikely
to be what the author of the generic intended.
Overall, I think the proposal only makes sense for (untagged) records.
And I am not too excited by the prospect of a configuration pragma:
it seems to me that users should give careful thought to the impact
of using this pragma, they should not just slap it in the configuration
and forget about it.
Incidentally, most (all?) of the cases where I have seen people want
to use fuzzy equality for floats were situations where they were too
lazy/incompetent to do proper error analysis. The fudge factors 0.99
and 1.01 look highly suspicious to me, and are likely to be bugs
waiting to happen.
****************************************************************
From: Robert Dewar
Sent: Wednesday, September 17, 2008 5:24 PM
> Incidentally, most (all?) of the cases where I have seen people want
> to use fuzzy equality for floats were situations where they were too
> lazy/incompetent to do proper error analysis. The fudge factors 0.99
> and 1.01 look highly suspicious to me, and are likely to be bugs
> waiting to happen.
fuzzy equality for floats is a bad idea period! I agree entirely that
this is a dubious idea patching over incompetent programming.
Many coding standards forbid equality on floats. That's also misguided.
Equality is well defined for floats in IEEE arithmetic, and many
algorithms legitimately use equality, e.g. for abosolute conversion in
algorithms where such conversion is expected.
****************************************************************
From: John Barnes
Sent: Monday, October 20, 2008 11:21 AM
Can I bring up the equality business again?
Much of the traffic on this topic has been about the problems of fuzzy
floats. The original example in the proposed AI was therefore unfortunate
by confusing the issue. It was about the general desire to control
composability and not meant to be about fooling around with floating
equality per se.
Below is another example not involving floats. It has two types Doctor_Type
and Appointment_Type. Both have equality redefined to ignore certain
components of the type. It then introduces a type Appointment hoping
that this type would use the redefined equality for the components but
it doesn't.
I think it was Pascal that suggested that the pragma should only be
applicable to nontagged records.
Incidentally I believe the RM says that all predefined types are composable.
Where is it? I seem to recall a discussion on this when doing Ada 95. Are
they composable because they behave as if the proposed pragma has been
applied (ie magic) or are they composable because the predefined equality
has not been messed about with?
---
with Ada.Calendar; use Ada.Calendar;
with Ada.Text_IO;
with Ada.Integer_Text_IO;
procedure Compose is
type Doctor_Type is record
Name : string (1..20) := "Unknown ";
Comments : string (1..20);
end record;
function "=" (Left, Right : Doctor_Type) return Boolean is
begin
if Left.Name = Right.Name then
return True;
else
return False;
end if;
end "=";
type Patient_Type is record
Name : string (1..20) := "Unknown ";
Comments : string (1..20);
end record;
function "=" (Left, Right : Patient_Type) return Boolean is
begin
if Left.Name = Right.Name then
return True;
else
return False;
end if;
end "=";
type Appointment_Type is record
Doctor : Doctor_Type;
Patient : Patient_Type;
Date : Time;
end record;
Appointment_1 : constant Appointment_Type := (
Doctor => (Name => "Oliver Cousins ",
Comments => "Neurologist "),
Patient => (Name => "Amy Cole ",
Comments => "Always late "),
Date => Time_Of (Year => 1988,
Month => 8,
Day => 8));
Appointment_2 : constant Appointment_Type := (
Doctor => (Name => "Oliver Cousins ",
Comments => "Nerve specialist "),
Patient => (Name => "Amy Cole ",
Comments => "Late again "),
Date => Time_Of (Year => 1988,
Month => 8,
Day => 8));
begin
Ada.Text_IO.Put_Line ("Doctors");
Ada.Text_IO.Put (Appointment_1.Doctor.Name);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put_Line ("and");
Ada.Text_IO.Put (Appointment_2.Doctor.Name);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put ("are ");
if Appointment_1.Doctor = Appointment_2.Doctor then
Ada.Text_IO.Put_Line ("the same");
else
Ada.Text_IO.Put_Line ("different");
end if;
Ada.Text_IO.Put_Line ("Patients");
Ada.Text_IO.Put (Appointment_1.Patient.Name);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put_Line ("and");
Ada.Text_IO.Put (Appointment_2.Patient.Name);
Ada.Text_IO.New_Line;
Ada.Text_IO.Put ("are ");
if Appointment_1.Patient = Appointment_2.Patient then
Ada.Text_IO.Put_Line ("the same");
else
Ada.Text_IO.Put_Line ("different");
end if;
Ada.Text_IO.Put_Line ("Appointments");
Ada.Text_IO.Put (Appointment_1.Doctor.Name);
Ada.Text_IO.Put (' ');
Ada.Text_IO.Put (Appointment_1.Patient.Name);
Ada.Text_IO.Put (' ');
Ada.Integer_Text_IO.Put (Year (Appointment_1.Date));
Ada.Text_IO.Put (' ');
Ada.Integer_Text_IO.Put (Month (Appointment_1.Date));
Ada.Text_IO.Put (' ');
Ada.Integer_Text_IO.Put (Day (Appointment_1.Date));
Ada.Text_IO.New_Line;
Ada.Text_IO.Put_Line ("and");
Ada.Text_IO.Put (Appointment_2.Doctor.Name);
Ada.Text_IO.Put (' ');
Ada.Text_IO.Put (Appointment_2.Patient.Name);
Ada.Text_IO.Put (' ');
Ada.Integer_Text_IO.Put (Year (Appointment_2.Date));
Ada.Text_IO.Put (' ');
Ada.Integer_Text_IO.Put (Month (Appointment_2.Date));
Ada.Text_IO.Put (' ');
Ada.Integer_Text_IO.Put (Day (Appointment_2.Date));
Ada.Text_IO.New_Line;
Ada.Text_IO.Put ("are ");
if Appointment_1 = Appointment_2 then
Ada.Text_IO.Put_Line ("the same");
else
Ada.Text_IO.Put_Line ("different");
end if;
end Compose;
****************************************************************
From: Tucker Taft
Sent: Monday, October 20, 2008 11:34 AM
I would be in favor of composable equality for all record types.
Currently only tagged record types compose, and I think that we were
just running scared of teeny incompatibilities in Ada 9x when we resisted
extending that to cover all record types. I agree with Pascal that making
user-defined operators on numeric types "compose" is asking for trouble,
but I don't see the problem for record types. Upward compatibility would
argue for a pragma, I suppose, but I would consider just doing it by fiat,
and let the chips fall where they may. I really can't imagine a case
where you wouldn't want record-type equality to compose, but I suppose
I am willing to hear about one.
****************************************************************
From: Edmond Schonberg
Sent: Monday, October 20, 2008 11:40 AM
But then why exclude it for array types?
****************************************************************
From: Robert Dewar
Sent: Monday, October 20, 2008 11:46 AM
> I really can't imagine a case
> where you wouldn't want record-type equality to compose, but I suppose
> I am willing to hear about one.
It's less an issue of an example that has some deliberate use of this
misfeature, than worrying about mysterious incompatibilities for existing
code.
Ed, how about we instrument GNAT to see how many cases in our test suite
would be affected by such a change
****************************************************************
From: Tucker Taft
Sent: Monday, October 20, 2008 11:56 AM
Array types are more difficult in my view. You can convert between
unrelated array types, and if the equality properties have to do with
something other than the component type, that might imply some special
situations with slices and/or sliding. I also worry that some array types
have operations besides equality (<, >, and, or, etc.). I am not
excited about trying to define how those compose.
Record types have at most one predefined operator that the user can
override explicitly (yes, I know about "/=" ;-), and that makes them
much simpler to deal with in my view.
****************************************************************
From: Randy Brukardt
Sent: Monday, October 20, 2008 12:00 PM
> But then why exclude it for array types?
The problem (as I understand it) is that it is nearly impossible to write
a correct generic if you cannot trust the relationships between the various
relational operators. And there is no composition of other operators. That's
not a problem for record types, because they don't have any other relational
operators other than equality. But (some) array types do have the other
ordering operators; since those don't compose, the same problem would occur
for them.
I suppose we could try to fix that problem by trying to figure out how
composition of the other predefined operators ought to work, but it surely
isn't as obvious as "=" is.
****************************************************************
From: Edmond Schonberg
Sent: Monday, October 20, 2008 12:18 PM
> Ed, how about we instrument GNAT to see how many cases in our test
> suite would be affected by such a change
We could check on all uses of predefined equality on records for which
some component has a user-defined equality. Is that what you have in mind?
****************************************************************
From: Robert Dewar
Sent: Monday, October 20, 2008 12:28 PM
yes, these are the cases where a change would make a difference
****************************************************************
From: Tucker Taft
Sent: Monday, October 20, 2008 12:37 PM
That would certainly be some relevant data.
****************************************************************
From: Edmond Schonberg
Sent: Monday, October 20, 2008 4:54 PM
Preliminary results: out of some 11719 relevant tests in the suite,
there are 23 that perform equality on a record that has some component
for which there is an explicit equality. Most of them are records
that contain an unbounded_string or an address. I'll look at some
of these to see whether proper composition would affect the results.
****************************************************************
From: Tucker Taft
Sent: Monday, October 20, 2008 5:05 PM
But both of these types are required to have composable equality
by the language definition (see RM 4.5.2(32.1/1)).
Any other cases where the types aren't language defined types?
****************************************************************
From: Randy Brukardt
Sent: Monday, October 20, 2008 5:07 PM
For nonlimited language-defined types, the language requires that they
compose properly (or act like it). See 4.5.2(31.1/1). So proper
composition should not affect the results of either of those types
(if it does, your compiler has a bug). A component would have to be a
user-defined type in order for it to matter.
****************************************************************
From: Edmond Schonberg
Sent: Monday, October 20, 2008 5:40 PM
> But both of these types are required to have composable equality by
> the language definition (see RM 4.5.2(32.1/1)).
And indeed they compose properly. no problem here. My mod to the
compiler did not special-case these types.
> Any other cases where the types aren't language defined types?
There are 6 tests with user-defined types, that I have to examine more
closely.
****************************************************************
From: Edmond Schonberg
Sent: Monday, October 20, 2008 8:52 PM
OK, there are 5 tests with equality on untagged record types that have
components with user-defined equality. Of these:
One is a query from a customer (many years ago) who was puzzled that
equality did not compose.
One of them is in GNAT code, and one of the operands is a constant that
represents the null value for the type. This works properly in this
case but it's slightly suspicious, and would in fact be better if equality
composed.
One of them is in the expanded code for a generated equality routine
with a subcomponent with user-defined equality.
Two of them are in Inspector code, and I'm sure they are correct :-)!
Conclusion from this small sample: having equality compose would be
harmless, and of course clearer conceptually.
****************************************************************
From: Randy Brukardt
Sent: Monday, October 20, 2008 9:14 PM
No ACATS tests? They wouldn't prove anything other than whether compilers
are actually doing this all the same way (no test would mean no certainty
of portability in this area).
****************************************************************
From: Robert Dewar
Sent: Monday, October 20, 2008 11:10 PM
Maybe we could just be extreme and declare that it was simply a typo
that equality did not compose and we forgot to fix it in Ada 95 and
Ada 2005 :-)
****************************************************************
From: Randy Brukardt
Sent: Monday, October 20, 2008 11:49 PM
That's tempting, but I think that the subtle incompatibility involved is
best being associated with a major version change to the language. I recall
someone else making the point that users are not likely to be surprised
about incompatibilities when moving between major versions (like Ada 95 to
Ada 2005), but are more likely to not be expecting changes when moving from
GNATPro 6.2 to 6.2.1 (or some similar minor compiler change).
The problem here is while we're pretty sure the change will fix more bugs
than it introduces, there is likely to be that one big project that somehow
managed to depend on this behavior.
I suspect that it would be better to tie the change to Ada 2005 (if we're even
that bold) or Ada 2014 (if we're not); that would have the advantage that
projects with problems could at least regress to the Ada 95 semantics
(presuming that most compilers have a way to get that).
****************************************************************
From: Robert Dewar
Sent: Tuesday, October 21, 2008 8:17 AM
Actually my view is that it is unacceptable to regard major version changes
as a source of incompatibilities. If we think of this as an incompatibility
that is only acceptable for a major version change, then it is not acceptable
for that either. The Ada 2005 design introduced serious incompatibilities
that are still causing trouble, and still acting as an impediment to adoption
of Ada 2005.
Users *ARE* surprised by significant incompatibilities between versions, and
a *MAJOR* inducement to moving to a new version of the language is to guarantee
that there are no such cases. Ada 2015 is very unlikely to have enough goodies
to induce change ten years out to cancel out worries about incompatibilites.
To me there are only two acceptable choices
a) decide this is a fix, and that therefore it is not only acceptable but
desirable to retrofit it to old versions. An implementor worried about the
incompatibility issue can introduce a switch to maintain the old "wrong"
behavior.
b) decide that this is an incoimpatible change, in which case it is not
acceptable unless triggered by a pragma, which should either be useable
as a global configuration pragma, or be applicable to a specified type.
****************************************************************
From: Jean-Pierre Rosen
Sent: Tuesday, October 21, 2008 8:59 AM
Or maybe:
c) decide that this is an incompatible but desirable change, with a
pragma to keep the *old* behaviour, for the rare cases where there
is a serious compatibility issue.
****************************************************************
From: Robert Dewar
Sent: Tuesday, October 21, 2008 9:06 AM
I would use a pragma for the new behavior. In new code wanting to use
this feature, chucking in a pragma is no sweat, but telling people they
have to modify existing code because of an incompatibilty we consider
significant seems undesirable.
BTW what was the original motivation behind the rule? Given that it does
not work for fpt to do block compares, the whole thing seems silly to me.
****************************************************************
From: Tucker Taft
Sent: Tuesday, October 21, 2008 9:32 AM
The decision in Ada 9X about tagged versus untagged was connected to a
number of relatively obscure technical issues.
In general, we required overriding of an operation of a tagged type to
be subtype conformant with the inherited operation, whereas Ada 83 only
required "type conformance" for overriding of operations of an untagged
type, which meant that even the modes of the parameters could differ.
The overriding of predefined operators obviously couldn't have mode
mismatches, since operators only allow IN parameters, but you could have
different subtypes at the least, though why you would want to override
with a more restrictive subtype is hard to imagine.
We wanted to treat operators the same way we were treating the operations
inherited from the ancestor type for a formal derived type, and we wanted
composability and the rules for reemergence in generic instances to match
up consistently.
If we step back now and see where the biggest "surprise" is, I think
predefined record-type equality "reemerging" in generics, and not composing
properly, is the one that remains. A surgical strike to eliminate that seems
like a good idea in my mind, and leave the other differences between
tagged and untagged types as they are. And I guess I agree with Robert,
this ought to be something that can be back-fitted to existing compilers.
Unlike the Ada 83 to Ada 95 transition, I see people "slipping" into
Ada 2005 features on a more incremental basis. Containers are very
attractive, and just getting them to compile either requires making
various changes to them to get them back into Ada 95 acceptability, or
starting to adopt at least some Ada 2005 features.
I think in retrospect it *was* a mistake to distinguish between tagged
and untagged record types as far as predefined equality, but it was hidden
behind some bigger distinctions that I believe were *not* a mistake.
A pragma is probably the right approach, but there is nothing saying that
it couldn't be the "default" configuration to have the pragma already in
place. Having a pragma that allows the user to specify either direction
is probably safest, so that if you say nothing, you get the default
behavior of the compiler (unless in ACATS mode of course where you get
the standard behavior which at this point is no composition), and then
you can specify either you want composition, or you rely on
non-composability.
****************************************************************
From: Tucker Taft
Sent: Tuesday, October 21, 2008 10:01 AM
By the way Robert, what have emerged as the biggest incompatibilities
in Ada 2005, in AdaCore's experience?
I presume the loss of by-reference function return is one. Others?
****************************************************************
From: Robert Dewar
Sent: Tuesday, October 21, 2008 10:30 AM
> By the way Robert, what have emerged as the biggest incompatibilities
> in Ada 2005, in AdaCore's experience?
> I presume the loss of by-reference function return is one. Others?
That's the really big one, can't remember any others that are causing
continued grief, Bob?
****************************************************************
From: Tucker Taft
Sent: Tuesday, October 21, 2008 9:54 AM
Gosh, I hate to say it, but this might argue for bringing back the idea
of a "Features" pragma... ;-)
The danger of having a single-purpose pragma for this is that if the
compiler does *not* support the feature, you don't get an error, but
instead just a warning that you have an unrecognized pragma. If we add
a general pragma that the compiler *must* support, such as "Restrictions,"
and then require that it fail if it sees the name of a feature it doesn't
recognize, we can give the user some comfort that if they are depending
on the presence of some particular feature, they will get an error if the
feature isn't supported.
We could use "No_<blah>" and "<blah>" to indicate a requirement that the
feature not be present, or that the feature be present. E.g.:
pragma Features(No_Record_Equality_Composability);
vs.
pragma Features(Record_Equality_Composability);
This pragma would cause an error if the feature name were not recognized.
****************************************************************
From: Robert Dewar
Sent: Tuesday, October 21, 2008 10:25 AM
I think I would prefer
pragma Composable_Equality (On | Off);
****************************************************************
From: Joyce Tokar
Sent: Tuesday, October 21, 2008 10:31 AM
Why not update pragma Restrictions to achieve this goal?
****************************************************************
From: Robert Dewar
Sent: Tuesday, October 21, 2008 10:43 AM
this really has nothing to do with a restriction, and I don't see a
way of recasting it in that mode, do you? Joyce?
****************************************************************
From: Tucker Taft
Sent: Tuesday, October 21, 2008 11:05 PM
Composable_Equality is going a bit too far, since I don't think we
want to imply that we will do this for non-record types.
You didn't comment on my point about recognizable pragmas -- that is
we want the user to be sure the pragma isn't just being ignored. I
also worry about a proliferation of special-purpose pragmas. Eventually
you want to be able to have a single place where you define what is
the set of features you are using.
With a pragma like "Features" and the existing "Restrictions," we could
have a pair of pragmas that pretty much define what is acceptable. If
we define a collection of possibly overlapping feature/restriction-ish
pragmas, really knowing what features are safely usable in a given
program may be tricky. When combined with the notion of a Profile,
we could get a nice triumvirate.
****************************************************************
From: Joyce Tokar
Sent: Tuesday, October 21, 2008 11:48 AM
Essentially, this is what I was driving at with my question about
pragma Restrictions --- and we know about restriction profile --
see Ravenscar.
****************************************************************
From: Robert Dewar
Sent: Tuesday, October 21, 2008 11:26 AM
What are other features you have in mind for this pragma, a pragma
with only one possibility seems silly to me.
what existing pragams could be subsumed,
also we want this pragma to be able to apply to a specific type,
how would that fit into your scheme.
I don't see any problem in proliferation, we have hundreds of pragmas
already!
****************************************************************
From: Tucker Taft
Sent: Tuesday, October 21, 2008 12:00 PM
I had imagined using a pragma Features for the various Ada 2005 features
which might be provided "piecemeal."
I suppose it would be most important for features that involve any kind
of potential semantic inconsistency, such as the return-by-reference vs.
build-in-place, but it could be useful as documentation for the set of
features expected to be fully supported. The danger is that some compiler
might accept the syntax for something like access-to-subprogram, but not
support the full downward closure semantics.
Or more obviously, whether null values can be passed to access parameters
might not be at all obvious, because we have encouraged Ada 95 compilers
to accept, and ignore, the "not null" syntax on access parameters. I can
see the same thing effectively happening with, say "private with,"
where the compiler would be augmented to accept the syntax, but treat it
as equivalent to simply "with." This is most tempting when trying to
compile the container packages, where you might be able to get away with
accepting the Ada 2005 syntax they use, but "punt" on the semantics where
possible.
I know I was tempted by that recently, because the latest GNAT sources
use container packages, and I was trying to compile them with AdaMagic to
host the gnat2scil on my laptop in the absence of a GNAT version for
Mac OS X that could compile the inspector.
****************************************************************
From: Edmond Schonberg
Sent: Tuesday, October 21, 2008 12:06 PM
>> Conclusion from this small sample: having equality compose would be
>> harmless, and of course clearer conceptually.
>
> No ACATS tests? They wouldn't prove anything other than whether
> compilers are actually doing this all the same way (no test would mean
> no certainty of portability in this area).
Indeed, no ACATS tests (either B or C) that have a comparison of
untagged records with a component that has a user-defined equality.
Given the state of this discussion, you might not want to rush and
add this to the test objectives of ACATS 3X :-)!
****************************************************************
From: Robert I. Eachus
Sent: Tuesday, October 21, 2008 1:46 PM
I think the idea of a pragma is best. That way with a wink an a nod,
the default behavoir for compilers can be equality always composing in
records.
I also think that proper names for the pragmas should make it clear which
one to use in your code if you are a belt and suspenders type..
(Only needed of course, if you define an equality operation for a type.)
pragma Equality_Composes( For => Base_Type); --seems right, with
-- no argument can be used as a configuration pragma.
The other way is harder:
pragma Equality_Does_Not_Compose(For => Base_Type); --seems wordy, and still not correct.
pragma Predefined_Equality_Reemerges(For => Base_Type); --not as scary for the uninitiated...
Any better suggestions? It would be nice to have something that looks
appropriate for Boolean.
****************************************************************
From: Randy Brukardt
Sent: Wednesday, October 22, 2008 8:14 PM
...
> BTW what was the original motivation behind the rule? Given that it
> does not work for fpt to do block compares, the whole thing seems
> silly to me.
I was hoping to answer this question, since I was wondering about that
myself. Tucker has answered why tagged types are different, but the more
interesting question is why Ada 83 adopted non-composition in the
first place. However, none of the on-line materials (such as the Ada 83
Rationale) say anything about equality at all (so the smaller question of
composition isn't answered either). I could speculate (This issue was never
really considered? a misguided attempt to reduce implementation complexity?
A worry about consistency of relational operations?) but that doesn't do
any good.
So, unless John Goodenough has the answer in his huge stack of paper
discussions [and finding it in there is not likely to be easy], I'm afraid
the reason is lost to the mists of time.
****************************************************************
From: Robert Dewar
Sent: Wednesday, October 22, 2008 8:21 PM
And as I noted, you can't in general do block compares because of float values
*anyway*, so the whole thing seems bogus
It is interesting that several of the cases we found in our test suite were
in our internal code, and in all cases, the author was VERY surprised and
we had latent bugs.
I more and more think we should do the following
a) fix the bug, for all versions of Ada
b) note that implementors may wish to provide a pragma to rever to the old
state of the language. We can suggest the form of this pragma, but I see
no reason to have it be part of the formal definition of Ada.
I don't believe there could be any real compatibility problems in practice,
on the contrary I think it is likely this will fix bugs!
****************************************************************
From: Tucker Taft
Sent: Wednesday, October 22, 2008 9:35 PM
You weren't allowed to redefine equality for non-limited types in Ada 83,
so the problem didn't arise. Why weren't you allowed to redefine equality?
I suspect because you weren't allowed to redefine assignment, and there
is an implicit connection between equality and assignment.
****************************************************************
From: Robert I. Eachus
Sent: Wednesday, October 22, 2008 10:11 PM
...
>So, unless John Goodenough has the answer in his huge stack of paper
>discussions [and finding it in there is not likely to be easy], I'm
>afraid the reason is lost to the mists of time.
If you are asking about the non-composition of equality in records, that is
certainly not lost. The intent was to allow comparison for records to be
implemented as a bit for bit* comparison. The same holds true for arrays,
at least for equality of arrays of boolean and enumeration types. I do
remember lots of discussion prior to Ada 83 being finalized about equality
for floating point in the presence of potentially unnormalized values.
But that basically went away with the hardware that permitted it. Both the
IEEE and DEC floating point formats have a single valid representation for
every value--at least within types. But there was a lot of hardware around
in the early eighties with non-IEEE formats, and some allowed mixing
unnormalized and normalized values. The hardware did the floating point
compares right--but the existence of an FP field in a record on such
hardware meant you couldn't use a bitwise compare.
The issues of implementing equality when there were unused or uninitialized
bits in a record, didn't really emerge until after Ada 83 was in its final
form. There was a similar implementation issue that showed up with the
80286. You could have pointers--excuse me access values--that designated
the same object which were not bit for bit identical. I don't think this
ever got resolved as such, since the 80386 encouraged, instead, the use of
a 32-bit flat address space. (You could ask Alsys how their '286 compiler
dealt with that. My guess is that the compiler only created one designation
for a heap object, and other equivalent pointer values were treated as
non-identical.) There was also an issue with the Motorola 860x0 family
addresses. They were technically 36-bit addresses, and the 16 address spaces
could point at different views of the same memory. However, AFAIK, this
feature was only used during boot up. The boot PROM was usually given its
own address space, then mapped above the RAM once virtual memory was
activated. AFAIK no 680x0 OS ever supported more than 32-bit addressing.
(I remember the 36-bit address details mostly because of changes between
the 68020 and 68030 in this area. I updated the Stratus assembler to
support 68030 chips, and at least half the work involved addressing modes
never used once VM started.)
* Of course, compilers were expected to optimize these compares to use byte,
16-bit, 32-bit, etc. compare instructions when possible. (Now, 64.
128. and soon 256-bit operations.) Early compilers, though, were doing
good if they used string compare instructions for String. Dave Emery
wrote a move for records in Ada that used 32-bit instructions (and
Unchecked_Conversion! ;-) for all but a few bytes, because the compiler
generated code was so slow in, I think, Verdix 4.1. Anyway when he showed
the results to Verdix, they incorporated his code into the compiler.
(After asking permission, and Dave publishing the code in a paper to
handle the rights issues. ;-)
****************************************************************
From: Robert Dewar
Sent: Thursday, October 23, 2008 7:30 AM
> If you are asking about the non-composition of equality in records,
> that is certainly not lost. The intent was to allow comparison for
> records to be implemented as a bit for bit* comparison. The same
> holds true for arrays, at least for equality of arrays of boolean and
> enumeration types. I do remember lots of discussion prior to Ada 83
> being finalized about equality for floating point in the presence of
> potentially unnormalized values. But that basically went away with
> the hardware that permitted it. Both the IEEE and DEC floating point
> formats have a single valid representation for every value--at least within types.
not true, +0.0 and =0.0 must compare equal!
****************************************************************
From: Randy Brukardt
Sent: Thursday, October 23, 2008 7:12 PM
> You weren't allowed to redefine equality for non-limited types in Ada
> 83, so the problem didn't arise.
> Why weren't you allowed to redefine equality? I suspect because you
> weren't allowed to redefine assignment, and there is an implicit
> connection between equality and assignment.
Well, obviously that wasn't true in practice (the Goodenough trick was widely
known and used to redefine non-limited "="), but I suppose it explains why
the issue wasn't seriously considered. But that then begs the question of
why you didn't just fix it in Ada 95 (any existing code would have had to
have been on the margins of the language) as opposed to leaving it broken
so that now it is far more likely to have occurred in practice. I suppose
compatibility concerns were brought up then, too, and you were very sensitive
to this kind of incompatibility.
****************************************************************
From: Robert Dewar
Sent: Thursday, October 23, 2008 7:20 PM
This reminds me a little of the Fortran discussion. In old Fortran, DO loops
always traveled at least one even if the high bound was less than the low bound.
This was ugly, and the Fortran committee wondered whether to change it.
They did an extensive survey and found one case where it mattered, and it was
a bug!
Similarly, our survey of existing code found only a couple of cases where it
mattered, and one was a latent bug, where the author thought it composed, but
luckily it happened to work anyway, and other was a real bug, where the author
thought it composed, and the fact that it didn't was a real bug.
Based on that I think we should do what the Fortran commmittee decided to do
.. fix the bug!
****************************************************************
From: Robert Dewar
Sent: Thursday, October 23, 2008 7:21 PM
Note incidentally that I am normally in a mode of being fanatic about
avoiding upward incompatibilities, so I am giving my thoughts on this
issue in light of that basic attitude.
****************************************************************
From: Randy Brukardt
Sent: Thursday, October 23, 2008 7:29 PM
I know. I've been wondering if someone else at AdaCore is using your e-mail account. ;-)
****************************************************************
From: Robert Dewar
Sent: Thursday, October 23, 2008 7:44 PM
Well compatibility is a pragmatic issue not a theoretical issue, so the issue
is will existing customers be affected? Sometimes we make changes (like 'Size
in Ada 95, which are not theoretically compatibility issues, but are in
practice). When we make inncompatibloe changes, the issue is not whether there
exist probels in theory, but whether these problems exist in practice.
As Yogi Berra said, In theory, practice and theory are the same, in practice
they are different.
****************************************************************
From: Randy Brukardt
Sent: Thursday, October 23, 2008 7:25 PM
> > If you are asking about the non-composition of equality in records,
> > that is certainly not lost. The intent was to allow comparison for
> > records to be implemented as a bit for bit* comparison. The same
> > holds true for arrays, at least for equality of arrays of boolean and
> > enumeration types. I do remember lots of discussion prior to Ada 83
> > being finalized about equality for floating point in the presence of
> > potentially unnormalized values. But that basically went away with
> > the hardware that permitted it. Both the IEEE and DEC floating point
> > formats have a single valid representation for every value--at least within types.
>
> not true, +0.0 and =0.0 must compare equal!
Not to mention that it doesn't work for one's complement Integers (as we found
out on the U2200 - +0 and -0 had better compare equal) nor for records with
holes (you could initialize the holes to zero, but record object creation is
much more likely than record comparison - at least in the code we looked at -
so optimizing the less likely operation doesn't make much sense). The idea that
you can alway use a bit comparison is bogus. You can use bit comparisons in
some cases (it is worth optimizing them to bit string comparisons, and in fact
we did so from day one), but you can't do so much of the time. It's much the
same as streaming in that sense (there is a lot of value to bit streaming of
a record, but you can only do it in limited cases).
****************************************************************
From: Robert Dewar
Sent: Thursday, October 23, 2008 7:42 PM
Right, so compilers already almost certainly have the needed circuitry.
I know that in GNAT this would be a VERY easy change to make, probably
a line or two.
****************************************************************
From: Steve Baird
Sent: Thursday, November 13, 2008 7:20 PM
At the Portland meeting we decided to treat untagged records in the
same way that tagged records have always been treated with respect to
the definition of the predefined "=" operator for an enclosing composite
type.
As I remember it, we did not discuss how this is supposed to work in the
case where the user-defined "=" op for the untagged record type is not
visible at the point of the declaration of the enclosing composite type.
Given this example,
package Pkg1 is
type T1 is record X, Y : Integer; end record;
private
function "=" (L, R : T1) return Boolean;
end Pkg;
with Pkg1;
package Pkg2 is
type T2 is record A, B : Pkg1.T1; end record;
end Pkg2;
how should T2's equality operator behave?
****************************************************************
From: Tucker Taft
Sent: Thursday, November 13, 2008 7:41 PM
For tagged types, we say that even if an overriding takes place in the private
part, the code executed for a call on a primitive operation is independent of
visibility. I think if we are going to try to make record equality work the
same whether the type is tagged or not, we should probably adopt this principle
for all record equality.
In other words, so long as the "=" is overridden somewhere in the visible or
private part of the package, it shouldn't matter whether it is visible or not.
In short: visibility shouldn't affect composability.
We should probably also adopt the "no late overriding" rule for record equality,
disallowing overriding after the type has been frozen (and definitely not
allowing it in a body).
****************************************************************
From: Randy Brukardt
Sent: Thursday, November 13, 2008 8:30 PM
That seems like adding a compatibility issue where one is not necessarily needed.
Wouldn't it make more sense to simply say that "late" overridings don't
participate in composition? Existing code with late equality definitions surely
doesn't depend on composition, and breaking it solely so composition (that they
don't use) works seems like a good way to get the idea dropped.
If we were starting from scratch, you surely would be correct. Overriding would
work very differently for untagged types (and then we wouldn't need reemergence
in generics, either). But as it is, I'd suggest a compiler warning on late
overriding and nothing more.
****************************************************************
From: Tucker Taft
Sent: Thursday, November 13, 2008 8:51 PM
I am only proposing "no late overriding" for record equality.
I think it is important to make the late overriding illegal, to avoid confusion
where the programmer might presume they are getting composable equality when
they aren't.
I think this is a very minor incompatibility, and it is clearly one that would be
caught by the compiler. If we believe that providing composability for equality
will fix more bugs than it will introduce, we should make illegal any situation
where you *don't* get composability when it seemed like you might. Having a
"half-and-half" situation seems dangerous, since there will be no easy answer
to what user-defined equality operators are composable. That isn't doing anybody
any favors.
By the way, when I use the term "late overriding" I mean for a type declared in
a package spec, overriding a primitive in the package body or after the type is
frozen. That's not allowed for tagged types, but it is allowed for untagged types
(and it's a bit of a pain to implement, by the way ;-).
****************************************************************
From: Randy Brukardt
Sent: Thursday, November 13, 2008 9:33 PM
> I am only proposing "no late overriding" for record equality.
That's all I thought.
...
> By the way, when I use the term "late overriding" I mean for a type
> declared in a package spec, overriding a primitive in the package body
> or after the type is frozen.
> That's not allowed for tagged types, but it is allowed for untagged
> types (and it's a bit of a pain to implement, by the way ;-).
I could see banning it in a spec (it's easy to fix by moving the declaration
up a few lines), but doing it in a body seems like its adding a compatibility
problem for no reason. No sane person would expect a routine that the compiler
doesn't even know about to participate in composibility.
And the routine might depend on stuff in the body, or might really only be
intended for local use. (I have debugging equality routines like that.) Making
the user put it into the spec (thus making it available to the world) or making
them rename it seems like changes that could only cause bugs (it surely isn't
going to fix any - they're not depending on composible equality). Remember,
the GNAT survey didn't find many instances of anyone using a potentially composed
equality; but that doesn't mean that there isn't use of redefined equality,
even locally only.
P.S. I wouldn't consider anything that happens in the body as "overriding"
anyway. "Overriding" is really about inheritance, and there isn't any of that
going on in a body.
****************************************************************
From: Tucker Taft
Sent: Thursday, November 13, 2008 9:51 PM
> P.S. I wouldn't consider anything that happens in the body as "overriding"
> anyway. "Overriding" is really about inheritance, and there isn't any
> of that going on in a body.
Ahhh, but there you are wrong! Primitives that are overridden *are*
inherited, even if the overriding happens very "late" (e.g. in the body).
E.g.:
package P is
type T is ... -- *not* tagged
end P;
package body P is
function "="(L, R: T) return Boolean is ... -- late overriding
type T2 is new T;
-- This gets the new "=".
...
end P;
Cool, eh? ;-)
I think this should be illegal for record equality.
Did you imply in your earlier message that you actually do this kind of thing?
Wouldn't you expect composability if it were legal?
****************************************************************
From: Randy Brukardt
Sent: Thursday, November 13, 2008 10:42 PM
> Cool, eh? ;-)
Are you suggesting a new ACATS test?? ;-)
(For the record, Janus/Ada passes this test, so I suspect there already is
such an ACATS test, probably from the Goodenough era. I've attached the full
test below if you're as curious as I was...)
> I think this should be illegal for record equality.
> Did you imply in your earlier message that you actually do this kind
> of thing? Wouldn't you expect composability if it were legal?
I surely didn't derive an untagged type in a body; I consider deriving
untagged types to be a sin just a bit below using a coextension. ;-) (The only
reason I've done it is to "rename" the primitives of a generic easily, and I'd
really have preferred to be able to do that without creating a new type. Dan E.
is right on that issue.) I did declare an untagged equality in a body for
debugging purposes (can't remember if it was named "=", though, although that
doesn't matter too much as it surely could have been). I did it because I
*only* wanted to debug stuff going on in that body, and I surely did not want
it affecting anything else. If I had wanted *that*, I'd have put it in the spec
so the world could see it! So I think your suggested new requirement would be
actively harmful in this particular case.
I don't want to imply that this is some sort of deal-breaker, because it's
not: user-defined record equality isn't that common, and besides, all new types
ought to be controlled anyway. :-) I just don't see the point of worrying about
what it does enough to want to break existing code -- I doubt there is much
composing going on in package bodies anyway, especially of types declared
locally.
----
with Ada.Text_IO;
procedure Tuck3 is
package P is
type T is record -- *not* tagged
C : Character;
end record;
end P;
package body P is
function "="(L, R: T) return Boolean is -- late overriding
begin
return True;
end "=";
type T2 is new T;
-- This gets the new "=".
Obj : T2 := (C => '1');
begin
Ada.Text_IO.Put_Line ("--- Test Tuck3 - Test late overriding"
& " of = for untagged types");
if Obj = (C => 'A') then
Ada.Text_IO.Put_Line ("-- OK");
else
Ada.Text_IO.Put_Line ("** Wrong = used!");
end if;
end P;
begin
null;
end Tuck3;
****************************************************************
From: Steve Baird
Sent: Friday, November 14, 2008 2:56 PM
> For tagged types, we say that even if an overriding takes place in the
> private part, the code executed for a call on a primitive operation is
> independent of visibility.
> I think if we are going to try to make record equality work the same
> whether the type is tagged or not, we should probably adopt this
> principle for all record equality.
At the risk of belaboring the obvious, I want to confirm that this is not
just about composability and the equality operators of enclosing composite
types. You are saying that a call to Pkg1."=" from outside of Pkg1 should
execute the user-defined "=" function. Correct?
I agree that this would imply that a post-freezing-point "=" declaration
that would have been illegal for a tagged type should also be illegal for
an untagged record type.
We seem to have implicitly broadened the scope of this AI; I just want to
make this explicit.
****************************************************************
From: Tucker Taft
Sent: Friday, November 14, 2008 3:13 PM
I agree, this could be perceived as broadening the AI, but it seems difficult
to define what composability means if it is affected by visibility. And it
would be pretty weird if the directly called equality didn't agree with the
composed equality. We could avoid this problem by disallowing not only
late overriding of record "=", but also disallowing overriding in the
private part. That would ensure that you would either get a compile-time
error, or always get the same semantics as Ada 2005 for a direct call on
"=". That might be a nice thing to be able to say.
Overriding in the private part would be weird for an untagged type, and might
imply some kind of weird intent, so it might be best to disallow it in exchange
for getting full composability.
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
I agree with your analysis.
Before I attempt wording, I would like to know if there is general agreement
that the attached !problem and !proposal sections describe where we want
to go with this AI. [This is version /02 of this AI - Editor]
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
From: Steve Baird
Sent: Monday, November 17, 2008 7:20 PM
****************************************************************
Questions? Ask the ACAA Technical Agent