Version 1.3 of ais/ai-00304.txt

Unformatted version of ais/ai-00304.txt version 1.3
Other versions for file ais/ai-00304.txt

!standard 12.5(8)          03-09-30 AI95-00304/01
!standard A.4.4
!standard 3.2.1(3)
!class amendment 02-07-11
!status work item 03-09-30
!status received 02-07-11
!priority Low
!difficulty Hard
!subject Reemergence of "=" in generics.
!summary
A new category of types is proposed called "aliased" types. Aliased types are like tagged types in the following ways:
1) They are always passed by reference; 2) Formal parameters allow 'Access and 'Unchecked_Access; 3) User-defined "=" overrides the predefined "="
completely, including in a generic and in equality of enclosing objects;
4) Formal derived types use the actual's primitives
rather than the ancestor's primitives;
5) Subtype conformance is required when overriding
an inherited or predefined primitive subprogram, as opposed to just type conformance.
The reserved word "aliased" may be used after "is" in a private type declaration, a formal private type declaration, and a composite type declaration. If a partial view is aliased, then the full view must be aliased. All tagged types are implicitly aliased types.
Individual stand-alone objects must still be explicitly marked "aliased" if 'Access is to be used with them.
!problem
There is a requirement that language-defined private types have "composable" equality, that is, if a type defined in a language-defined package is used as a component of another type, then equality on the enclosing type "works right." The most relevant example is bounded strings, where equality is defined in terms of the characters of the abstract string represented, as opposed to the underlying representation, which may have additional padding characters which should not participate in the comparison.
Tagged types "work right" in this context, where a user-defined equality operator is used in place of the predefined equality operator everywhere, including in generic instances and in equality for enclosing objects. What is desired is a way to have the same "correct" semantics for user-defined equality, but without making the type into a tagged type, which has potentially undesired implications (e.g. no defaults for discriminants, type descriptors, implicitly generated stream attribute functions, support for Internal_Tag, etc.).
Once equality operators "work right" in generics, it would be natural to want other user-defined primitives to work right for formal derived types (that is, the actual's rather than the ancestor's primitives are used in the instance).
Finally, there is a generally unrelated need on occasion to ensure that a type is always passed by reference, and that 'Access or 'Unchecked_Access can be used meaningfully on formal parameters of the type. Tagged types also have this characteristic, and hence there might be some value in supporting these two desires with a single solution.
!proposal
See summary.
!wording
TBD.
!example
Here is the bounded strings example:
package Ada.Strings.Bounded is generic Max : Positive; package Generic_Bounded_Length is ... type Bounded_String is private; ... private function "="(Left, Right : Bounded_String) return Boolean; type Bounded_String is aliased record -- "aliased" ensures no reemergence of predefined "=" Length : Natural range 0..Max := 0; Data : String(1..Max); end record; ...
By using "aliased" we ensure that the user-defined "=" which presumably only compares BStr.Data(1..BStr.Length) will be used in all cases. Without this, either the bounded string will have to be a tagged type, or Data will have to be default-initialized to all zeros, something which could add substantially to the cost of using the type if Max is relatively large.
---------
Here is an example of a type that is used to provide top-down "context" while processing a tree representation of a program or other modular construct. There happens to be a user-defined "=" operator, and a need for the type to be passed by reference, with 'Access allowed on parameters.
I'll admit this is a bit of a farfetched example, but I have certainly encountered something similar to this in real static analysis programs.
package Contexts is
type Context is aliased private; -- full type must be aliased type Context_Ptr is access all Context;
procedure Initialize_New_Context(Ctx : in out Context; Current_Unit : Unit_Type; Is_Read_Only : Boolean := False; Enclosing_Context : Context_Ptr := null);
function "="(Left, Right : Context) return Boolean; -- compares Current_Unit and Is_Read_Only; ignores Enclosing_Context pointer
function Is_Read_Only(Ctx : Context) return Boolean; function Current_Unit(Ctx : Context) return Unit_Type; function Enclosing_Context(Ctx : Context) return Context_Ptr;
private
type Context is aliased record Enclosing_Context : Context_Ptr; Current_Unit : Unit_Type; Is_Read_Only : Boolean; end record;
end Contexts;
with Contexts; use Contexts; package body Analyzer is procedure Process_Unit(Unit : Unit_Type; Cur_Context : in out Context) is Local_Context : Context; begin Initialize_New_Context(Local_Context, Current_Unit => Unit, Enclosing_Context => Cur_Context'Unchecked_Access); ... Process_Innards(Unit, Cur_Context => Local_Context); ... end Process_Unit; ...
!discussion
We considered just making all record types "work right" but there were felt to be too many possibilities for subtle incompatibilities.
One interesting side-effect of having a category of formal private types that is always passed by reference, and cannot be matched by an elementary type, is that a particularly simple form of generic sharing would be enabled by it. If the formal types of a generic were all aliased private, then pointers could be used for everything, and the code could relatively easily be constructed to be sharable.
!ACATS Test
ACATS tests are needed for these features.
!appendix

From: Tucker Taft
Sent: Thursday, June 13, 2002  8:31 PM

Robert A Duff wrote:

> Randy said:
>
> > Deleting bounded strings would make more sense. :-)
>

...

> But the implementation of Bounded_Strings initialized all 1000
> characters (because some AI says "=" has to compose on these things)!

It might be time to address this issue directly, namely that "=" reemerges in
generics, and doesn't compose.  We have special rules for the "=" operator for
tagged types, why not make this operator special for all types, requiring that
if user-defined using the base subtype for scalar formals, or the first subtype
for other types, then the predefined one does *not* reemerge in generics, and
it composes properly.

Yes, I realize this would not be upward compatible, and would not be
the same as other operators (but those don't *need* to compose), but
remember that until Ada 95, it was very difficult to define your own equality
operator anyway on a non-limited type.

At the very least, we should do this for record and private types.  And of
course, once you do it for private types, things get weird if you don't
do it for all types.

****************************************************************

From: Tucker Taft
Sent: Thursday, June 13, 2002  9:25 PM

Anticipating the "shared generics" implementation problems,
I humbly recast this as a proposal to make user-defined "="
compose properly and hide the predefined "=" in generics
only for record types (and for private types that are record
types "deep down").

****************************************************************

From: Randy Brukardt
Sent: Thursday, June 13, 2002 10:27 PM

> Anticipating the "shared generics" implementation problems,

Nice to hear that some people are responding to the training. :-)

> I humbly recast this as a proposal to make user-defined "="
> compose properly and hide the predefined "=" in generics
> only for record types (and for private types that are record
> types "deep down").

From a shared generics perspective, it would make more sense to simply have
this happen for generic formal private types (and maybe generic formal
derived record types?). After all, we'd need to use a thunk for any generic
formal private "=", so why shouldn't all types work right in that context.
Redefinition of "=" on elementary types is a lot rarer, and both the
compatibility issues and the runtime cost wouldn't be worth it.

But my gut feeling is that this is too incompatible to consider. People have
been working around this problem since time immemorial (i.e. 1983), and we'd
have to make sure that all of the workarounds still worked.

****************************************************************

From: Pascal Leroy
Sent: Saturday, June 15, 2002  4:46 AM

> Yes, I realize this would not be upward compatible, and would not be
> the same as other operators (but those don't *need* to compose), but
> remember that until Ada 95, it was very difficult to define your own
> equality operator anyway on a non-limited type.

Well, Ada 95 has been around for 7 years, and the Ada 83 trick was well
publicized at the time, so I think that we cannot afford the
incompatibility.

On the other hand, if we had a mechanism to specify, on the declaration of
"=", that it has to compose and doesn't reemerge, that would be both
compatible and useful.  It could be done by some sort of
pragma/attribute/new syntax/whatever.

****************************************************************

From: Bob Duff
Sent: Saturday, June 15, 2002  7:24 AM

As a pragma, it violates Bob Duff's Rule of Good Taste in Pragmas,
which is, "Pragmas should not have a strong effect on high-level
semantics."

As new syntax, it's ugly, because you always want it.

But I admit that one of these is probably the best solution,
given the (rather subtle) upward compatibility concern.

****************************************************************

From: Pascal Obry
Sent: Saturday, June 15, 2002  7:24 AM

Right, but would be quite cleaner than introducing a pragma. In such
a case the compiler could certainly warn and point out where the
compatibility problems are, no ?

****************************************************************

From: Bob Duff
Sent: Sunday, June 16, 2002 12:17 PM

> Right, but would be quite cleaner than introducing a pragma.

Sorry, I don't understand what you mean.

>... In such
> a case the compiler could certainly warn and point out where the
> compatibility problems are, no ?

Yes, a compiler could warn if you pass a type with user-defined "=" to a
generic, and call "=" in the generic.  Also if you declare a record or
array component with user-defined "=", and call "=" on the outer thing.

In fact, such a warning might be useful even if the language is *not*
changed.

****************************************************************

From: Tucker Taft
Sent: Sunday, June 16, 2002 12:17 PM

> Sorry, I don't understand what you mean.

I meant that I think it would be better to "fix" the language by introducing
an slight incompatibility (the compiler will issue a nice warning to help
the migration) than to have a new pragma that change the semantic.

> In fact, such a warning might be useful even if the language is *not*
> changed.

Agreed.

****************************************************************

From: Robert Dewar
Sent: Sunday, June 22, 2002 11:12 AM

I am *completely* opposed to ANY deliberate incompatible changes at this stage.
None of the "improvements" to Ada being discussed is remotely worth the effort
of dealing with having to make changes in large legacy programs as a result
of gratuitous incompatibilities.

If we could not successfully argue for this incompatible change for Ada 9X, it
is sheer folly to consider sneaking it in for the next version of Ada.

Speaking for ACT, if any updated version of Ada has incompatibities of this
nature it would be sufficient for us to simply ignore the new feature. We
cannot afford to affect existing customers in this manner.

The idea that when you recompile millions of lines of legacy code that a
warning will be sufficient help to safely change code with minimal effort
is bogus!

****************************************************************

From: Tucker Taft
Sent: Tuesday, September 30, 2003 10:22 AM

In the past few weeks, there has been some
discussion of the pain associated with reemergence
of "=" when trying to create (untagged) private types whose
equality "composes" properly.  The problem is that
the predefined "=" compares all components, which
is painful for things like bounded strings, where
parts are potentially uninitialized.

One possibility is to change the rules for (untagged)
composite types so they match the rules for tagged types, and
say that a user-defined "=" overrides completely
the predefined "=", preventing it from reemerging
in generics or in the equality for enclosing types.
Unfortunately, this could have subtle incompatibilities
(though I tend to believe it would fix many more
subtle bugs than it would cause).

In any case, this leads to a desire to "mark" the
type or the "=" operator in some way to say that
it *really* overrides the predefined operator, now
and forever.

One possibility is to have a kind of type that
"works right" as do tagged types, but not have
the overhead of a tag or the possibility of type
extension.

In the mean time, there has been some talk about
having types that are always passed by reference,
and for which 'Access is defined for formal parameters,
which again matches the semantics of tagged types.

The natural suggestion for this latter situation is
to allow "aliased" to be used on type declarations,
roughly in the same syntactic location as the word
"tagged" is used, namely right after the "is" and
before "record", "private", or "limited" (and perhaps
also before "array".  E.g.:

    type T is aliased record
        ...
    end record;

Parameters of type T would always be passed by reference,
and the 'Access attribute would be permitted on formal
parameters of type T, and 'Address would always be meaningful.
All tagged types are implicitly "aliased," so an "aliased"
private type could be completed with a tagged full type.  Note
that stand-alone objects would still need "aliased" if 'Access
is to be applied to them, as is true now for "tagged."

Note also that aliased private types could *not* be completed
by elementary types (since they are passed by copy).
That could be interesting, as it might enable a poor-man's
generic sharing, if the formal types are all aliased private,
the implementation can safely use pointers and pass by
reference everywhere in the shared code body.

Perhaps we can kill two birds with one stone here.
Perhaps we can say that this new kind of type ("aliased",
or perhaps some other term), not only has by-reference
parameter passing and aliased formals, but also has
rules for overriding of primitives that requires subtype
conformance (as required for tagged), thereby allowing
generics to always use the user-defined primitive of
the actual type, rather than having the predefined operator,
or ancestor primitive, reemerge in generics (and enclosing
equality ops).

Bob Duff and I used to say that "tagged types work right" when
it comes to generics.  We could generalize this to say
"aliased types work right."  That is, a user-defined "="
for an aliased type would hide the predefined "=" now
and forever.  Also, for a formal derived aliased type, the
actual's primitive operations rather than the ancestor's
primitives would be used in the generic instance.  Finally,
when overriding a primitive of an aliased type, the parameter
profile of the overriding must be subtype conformant, not just
type conformant.

Note that presuming we define all tagged types to be
implicitly "aliased" types, then the number of rules
in the manual can stay about the same, its just some of them are phrased
in terms of "aliased" and "non-aliased" types rather than
"tagged" and "untagged" types.

Any comments?

****************************************************************

From: Randy Brukardt
Sent: Tuesday, September 30, 2003  5:22 PM

This is AI-00304, assigned to one Tucker Taft. Please feel free to do your
homework. :-)

My first reaction is that the idea is OK, but seems like killing a gnat (er,
fly) with a cannon. Detailed reactions will have to wait until after the
meeting.

****************************************************************

From: Tucker Taft
Sent: Tuesday, September 30, 2003  8:16 PM

Oh, thanks for the pointer.  Here is a quick draft of AI-304.

[This is version /01 of the AI.]

****************************************************************

From: Randy Brukardt
Sent: Tuesday, September 30, 2003  8:32 PM

> One interesting side-effect of having a category of formal
> private types that is always passed by reference, and cannot
> be matched by an elementary type, is that a particularly
> simple form of generic sharing would be enabled by it.
> If the formal types of a generic were all aliased private,
> then pointers could be used for everything, and the code
> could relatively easily be constructed to be sharable.

Which of course is how Janus/Ada works for private types. If the actual type is
pass-by-copy, we 'fake' pass by copy at the call site.

****************************************************************




Questions? Ask the ACAA Technical Agent