Version 1.7 of ais/ai-00359.txt

Unformatted version of ais/ai-00359.txt version 1.7
Other versions for file ais/ai-00359.txt

!standard 12.03 (12)          04-06-07 AI95-00359/03
!standard 12.03 (20)
!standard 13.14 (05)
!class amendment 03-07-22
!status No Action (9-0-0) 04-09-19
!status work item 03-09-29
!status received no action 03-07-22
!status received 03-04-14
!priority Low
!difficulty Medium
!subject Deferring Freezing of a Generic Instantiation
!summary
To better support generic signature packages, and mutually dependent types that involve generic instantiations, it is proposed that a generic instantiation not cause freezing until specified if a Deferred_Freezing_Point pragma applies to the instantiation.
For such an instantiation, no special freezing happens at the point of the instantiation, other than the freezing that is due to the elaboration of the declarations within the "spec" of the instantiation. The freezing of the instantiation as a whole takes place at the place of the pragma, at which point the instance is frozen and the instance body is elaborated.
!problem
Ada 95 provides formal package parameters. One way of using these parameters is to define a "signature" for a class of abstractions, such as all set abstractions, or all physical unit abstractions, and then build a new generic abstraction on top of the original class of abstractions using this signature as the template for a formal package parameter.
Unfortunately, it is difficult to use signatures because of the fact that an instantiation freezes all of its actual parameters.
For example:
Given the signature for a set abstraction:
generic type Element is private; type Set is private; with function Size(Of_Set : Set) return Natural is <>; with function Union(Left, Right : Set) return Set is <>; with function Intersection(Left, Right : Set) return Set is <>; with function Empty return Set is <>; with function Unit_Set(With_Item : Element) return Set is <>; with function Nth_Element(Of_Set : Set) return Element is <>; package Set_Signature is end;
... we could define a generic that required some set abstraction, but it didn't care which one so long as it implemented the above signature:
generic with package Base_Set is new Set_Signature(<>); package Layered_Abstraction is type Cool_Type(Set : access Base_Set.Set) is limited_private; procedure Massage(CT : in out Cool_Type; New_Set : Base_Set.Set); ...
end Layered_Abstraction;
Now if we want to define a set type and provide the pre-instantiated signature for it, we run into trouble:
generic type Elem is private; with function Hash(El : Elem) return Integer; package Hashed_Sets is type Set is private; function Union(Left, Right : Set) return Set;
...
package Signature is new Set_Signature(Elem, Set); private type Set is record
... end record; end Hashed_Sets;
The problem is that we can't do the instantiation of Set_Signature where we would want to do so, because the instantiation freezes the type "Set" prematurely.
[For an extended example of use of signatures, see the physical units AI (AI-00324).]
A similar problem occurs when a type wants to include a pointer to a container based on the type being defined. For example:
package Expressions is type Expr_Ref is private;
package Expr_Sequences is new Sequences(Expr_Ref); -- Freezing trouble here! type Seq_Of_Expr is access Expr_Sequences.Sequence;
function Operands(Expr : Expr_Ref) return Seq_Of_Expr;
...
private type Expression; -- completion deferred to body type Expr_Ref is access Expression;
end Expressions;
Here we have a case where we want to instantiate a generic using a private type, and use the results of the instantiation as the designated type of an access type, which is in turn used as a parameter or result type of an operation on the private type.
Unfortunately, we can't instantiate the "Sequences" generic with Expr_Ref, since it is private.
!proposal
(See summary.)
!wording
Add after 13.14(19/1):
The Deferred_Freezing_Point pragma may be used to specify a deferred point where the freezing caused by a generic_instantiation takes place. The elaboration of the instance body is also deferred to this point.
Syntax Rules
The form of a pragma Deferred_Freezing_Point is as follows:
pragma Deferred_Freezing_Point(generic_instantiation_local_name);
Static Semantic Rules
In the absence of a pragma Deferred_Freezing_Point, the occurrence of a generic_instantiation immediately causes freezing. However, if a pragma Deferred_Freezing_Point applies to the instantiation, the freezing is deferred to the place of the pragma. The only freezing that takes place at the point of the instantiation is that associated with the constructs of the instance declaration.
Legality Rules
A pragma Deferred_Freezing_Point that applies to a given generic_instantiation shall precede any construct (outside the instance itself) that causes freezing of an entity defined by the generic_instantiation.
Dynamic Semantic Rules
In the absence of a pragma Deferred_Freezing_Point, the elaboration of an instance body takes place immediately after elaborating the instance declaration, at the point of the generic_instantiation. However, if a pragma Deferred_Freezing_Point applies to the instantiation, the elaboration of the instance body is deferred to the place of the pragma.
AARM NOTE:
Even though elaboration of the instance body is deferred, no freezing reference to an entity defined by the instantiation is permitted, and hence no calls on such an entity nor other uses that might result in an access before elaboration error can occur prior to the elaboration of the instance body.
!example
(See problem.)
!discussion
Signature packages can be a very useful feature, but if they can't be instantiated in the generic that defines the abstraction, they become of much less use.
Generics in general can be very useful, but the freezing rules mean that they are not as powerful as cut-and-paste, since they can't be applied to private types before the private type is completely defined.
This proposal makes it possible for a generic instantiation to be used in more places than before, since it can be used with a private type before the type is completely defined. As things stand now, it is often necessary to completely restructure things to be able to use a generic container as part of defining a private type.
We considered making the freezing occur on first freezing reference to an entity defined by the instantiation, but that would make the point of elaboration of the instance body dependent on the mysteries of the freezing rules. Here we make the location of the pragma itself the point where freezing and body elaboration take place.
Because elaboration of the instance body occurs at the same point as freezing, implementations may continue to omit "elaboration bits" for subprograms defined by an instantiation.
--!corrigendum 13.14(05)
!ACATS test
An ACATS test should be created for this pragma.
!summary
!appendix

From: Tucker Taft
Sent: Wednesday, February 12, 2003  5:50 PM

I talked about this AI a bit with Pascal in Padua.
Here is a first cut at it.  The basic idea is to
relax the freezing rules associated with generic
instantiations, so signatures can actually be used
in a reasonable way.

---

!standard 13.14     (05)           03-02-12  AI95-<<melting_instantiations>>/01
!class amendment 03-02-12
!status work item 03-02-12
!status received 03-02-12
!priority Medium
!difficulty Medium
!subject Deferring Freezing of a Generic Instantiation

!summary

To better support generic signature packages, and
mutually dependent types that involve generic instantiations,
it is proposed that a generic instantiation not cause
freezing immediately if any of its actual parameters
are types that have not been completely defined.

For such an instantiation, no special freezing happens
at the point of the instantiation, other than the freezing
that is due to the elaboration of the declarations within
the "spec" of the instantiation.   Elaboration of the body
of the instantiation is deferred until a "general" freezing
point occurs, which occurs at the end of the enclosing
library unit or declarative part, upon encountering a "real" body, etc.

NOTE: This more "relaxed" freezing and deferred body elaboration
could be used for all generic instantiations, but that could
create some incompatibilities, since we would be altering
the point when the body of the instantiation was elaborated.
This proposal is guaranteed to be upward compatible, since
only instantiations that are currently illegal would be
using "relaxed" freezing and deferred body elaboration.

!problem

Ada 95 provides formal package parameters.  One way of using
these parameters is to define a "signature" for a class of abstractions,
such as all set abstractions, or all physical unit abstractions, and
then build a new generic abstraction on top of the original
class of abstractions using this signature as the template for
a formal package parameter.

Unfortunately, it is difficult to use signatures because
of the fact that an instantiation freezes all of its
actual parameters.

For example:

  Given the signature for a set abstraction:

    generic
	type Element is private;
	type Set is private;
	with function Size(Of_Set : Set) return Natural is <>;
	with function Union(Left, Right : Set) return Set is <>;
	with function Intersection(Left, Right : Set) return Set is <>;
	with function Empty return Set is <>;
	with function Unit_Set(With_Item : Element) return Set is <>;
	with function Nth_Element(Of_Set : Set) return Element is <>;
    package Set_Signature is end;

  ... we could define a generic that required some set
  abstraction, but it didn't care which one so long
  as it implemented the above signature:

    generic
	with package Base_Set is new Set_Signature(<>);
    package Layered_Abstraction is
	type Cool_Type(Set : access Base_Set.Set) is limited_private;
	procedure Massage(CT : in out Cool_Type; New_Set : Base_Set.Set);
        ...

    end Layered_Abstraction;

  Now if we want to define a set type and provide the pre-instantiated
  signature for it, we run into trouble:

    generic
	type Elem is private;
	with function Hash(El : Elem) return Integer;
    package Hashed_Sets is
	type Set is private;
	function Union(Left, Right : Set) return Set;
	...

	package Signature is new Set_Signature(Elem, Set);
    private
	type Set is record
	   ...
	end record;
    end Hashed_Sets;

The problem is that we can't do the instantiation of Set_Signature
where we would want to do so, because the instantiation freezes
the type "Set" prematurely.

[For an extended example of use of signatures, see the physical
units AI (AI-00324).]

A similar problem occurs when a type wants to include a pointer
to a container based on the type being defined.  For example:

    package Expressions is
	type Expr_Ref is private;

	package Expr_Sequences is new Sequences(Expr_Ref);
	    -- Freezing trouble here!
	type Seq_Of_Expr is access Expr_Sequences.Sequence;

	function Operands(Expr : Expr_Ref) return Seq_Of_Expr;

	...

   private
	type Expression;  -- completion deferred to body
	type Expr_Ref is access Expression;

   end Expressions;

Here we have a case where we want to instantiate a generic
using a private type, and use the results of the instantiation
as the designated type of an access type, which is in turn
used as a parameter or result type of an operation on the
private type.

Unfortunately, we can't instantiate the "Sequences" generic
with Expr_Ref, since it is private.

!proposal

(See summary.)

!wording

(See summary.)

!example

(See problem.)

!discussion

Signature packages can be a very useful feature, but
if they can't be instantiated in the generic that defines
the abstraction, they become of much less use.

Generics in general can be very useful, but the freezing
rules mean that they are not as powerful as cut-and-paste,
since they can't be applied to private types before
the private type is completely defined.

This proposal makes it possible for a generic instantiation
to be used in more places than before, since it can
be used with a private type before the type is completely
defined.  As things stand now, it is often necessary to
completely restructure things to be able to use a generic
container as part of defining a private type.

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

From: Robert A Duff
Sent: Wednesday, February 12, 2003  7:09 PM

As a user of Ada, I would find this relaxation of the freezing rules to
be of *huge* benefit.

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

From: Tucker Taft
Sent: Sunday, September 28, 2003  4:24 PM

Here is an update to an AI that never made
it into the AI database.  (I think the editor might
be doing some editorializing ... ;-).
Basically, I added wording.

Personally, I think this is an important AI,
because it enables an important use of generic
signatures on private types, but also makes
generics more flexible in general.

[Editor's note: This is version /02 of the AI.]

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

From: Pascal Leroy
Sent: Sunday, September 28, 2003  4:24 PM

If you look at the procedures, you'll see that "if the Editor, in
consultation with the Rapporteur, determines that a comment ...  is of
interest to only a tiny minority of users, the commentary is given a
status of Received No Action".  This is what happened to your previous
submission on this topic, and it was filed as AC95-00060/01.  The
procedures specify that, to resurrect this proposal from the dead, it
must have the support from two ARG members.  So you need to find someone
else who is excited about mucking with the freezing rules.

I am not trying to make a nuisance of myself, but as we are nearing the
deadline for adding new stuff to the Amendment, I am going to insist
that we go by the book: there is no point in spending precious ARG time
on proposals that don't have a modicum of support from the group.

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

From: Tucker Taft
Sent: Monday, September 29, 2003  7:16 AM

Well if I remember correctly, Bob Duff
said "attaboy!" when I submitted this proposal.
So I only need one more ;-).

(I promise I didn't intend to violate procedures,
but I was unaware that it had been classified
as "no action."  I thought that was reserved for
proposals that clearly had little merit.  I had the
sense that mine was a bit above that level, but
apparently not...)

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

From: Tucker Taft
Sent: Monday, September 29, 2003  2:39 PM

As I said Bob Duff I believe expressed support for this proposal.
Is that enough, or do you want another?  If so, anyone out there
interested in making generics more useful by relaxing these
freezing rules?  Please speak up now!

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

From: Robert A. Duff
Sent: Monday, September 29, 2003  3:37 PM

> As I said Bob Duff I believe expressed support for this proposal.
> Is that enough, or do you want another?  If so, anyone out there
> interested in making generics more useful by relaxing these
> freezing rules?  Please speak up now!

Yes, I do support this.

Put your user hats on, please, and take off your implementer and
language lawyer hats.  This is one of the most frustrating parts of Ada.

I want to write "package P is new G(T);" in the same package as T.
But that's illegal.  So a huge amount of restructuring is required.

Quite often, "G" is some sort of "growable sequence of T", or whatever,
and T *contains* such a sequence.  So it's no good to put P in a child.
Type T *needs* visibility upon the instance, P.

Note that if "sequence of T" happened to be "array of T", all would be
well.  But surely, replacing "array of T" with "Growable_Seq_Of_T"
should not require such major restructuring of the code.

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

From: Gary Dismukes
Sent: Monday, September 29, 2003  3:44 PM

I'll add my second.  I agree this is worth discussing.

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

From: Randy Brukardt
Sent: Monday, September 29, 2003  5:45 PM

> I want to write "package P is new G(T);" in the same package as T.
> But that's illegal.  So a huge amount of restructuring is required.

Tweet!!! Excessive use of generics! 15 yard penalty from the spot of the
foul!

(Sorry, it's football season. :-)

No, that has never, ever happened to me as an Ada user. OK, I can see how it
could happen with some models. But I think you're overstating the likelihood
of this case in practice. This is just like the 'problem' discussed in AARM
13.14(19.q-s), in which the premise is faulty (it's not important, no one
writes code like that), which of course leads to faulty rules which are
unnecessarily hard to implement.

But, merits of the problem aside, does it bother others (like it does me)
that the proposed 'solution' is to change the freezing and elaboration rules
of the language in order to 'fix' what is essentially a visibility problem?
What Bob needs is a partial view of a generic to go along with partially
viewed type.

This proposal has two serious technical problems:

1) Changing some unrelated type far away from an instantiation silently
changes the semantics of the instantiation. A program can start failing with
Program_Error simply because a type was changed to be a private type.

2) The changes to the elaboration model of instantiations are substantial.
Ada 83 is carefully designed so that once an instantiation is elaborated, no
elaboration checks are needed on the use of anything in that instantiation.
Changing that will have gigantic effects all over compilers - most of the
elaboration assumptions will be invalid. (I wonder what this would do to the
GNAT static elaboration model?)

And then there is freezing and generics. This is a dead body issue for me -
relaxing the freezing rules is simply impossible: we're generating the code
for a generic at the point that it is seen.

So, I'll be bringing an extra shoe along to the meeting (I don't want to
cause anyone to pass out by taking mine off).

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

From: Randy Brukardt
Sent: Monday, September 29, 2003  8:59 PM

Here's part of a note that I sent to Pascal when we were discussing what to do
with this proposal:

----

Anyway, it occurs to me that the main problem could be solved with an
"incomplete" instantiation. To show Tucker's original example:

    generic
        type Elem is private;
        with function Hash(El : Elem) return Integer;
    package Hashed_Sets is
        type Set is private;
        function Union(Left, Right : Set) return Set;
        ...
        incomplete package Signature is new Set_Signature(Elem, Set);
    private
        type Set is record
           ...
        end record;
        package Signature is new Set_Signature(Elem, Set);
    end Hashed_Sets;

(Syntax to be chosen.) This would make the instantiation available anywhere
after it is completed (which would include all external users), but nothing
from it could be used until that point. (We could go further and export
incomplete types or something like that, which would fix Tucker's second
example, but I don't think that complication is really necessary.)

The point is to allow an instantiation that has to be elaborated in the private
part still be to visible; it also makes the deferred elaboration crystal clear.
(Of course, it isn't really deferred.)

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

From: Robert Dewar
Sent: Monday, September 29, 2003  9:00 PM

> 2) The changes to the elaboration model of instantiations are substantial.
> Ada 83 is carefully designed so that once an instantiation is elaborated, no
> elaboration checks are needed on the use of anything in that instantiation.
> Changing that will have gigantic effects all over compilers - most of the
> elaboration assumptions will be invalid. (I wonder what this would do to the
> GNAT static elaboration model?)

AARGH! indeed this would be a major implementation pain, that's very
delicate code, and I think the result would be serious upwards
incomaptibilities with existing elaboration rules. This would need
VERY careful looking at.

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

From: Tucker Taft
Sent: Monday, September 29, 2003  9:51 PM

This seems to be exactly the kind of discussion that
is best done in person at an ARG meeting.  I hope we
will get some time on the agenda for this (but I
recognize we should put a time limit on the discussion).


Randy Brukardt wrote:

> I said:
>
>
>>But, merits of the problem aside, does it bother others (like it does me)
>>that the proposed 'solution' is to change the freezing and elaboration
>
> rules
>
>>of the language in order to 'fix' what is essentially a visibility
>
> problem?

I don't see this as a visibility problem.  It is
exactly a freezing problem.  The freezing rules for generic
instantiation effectively require that the actual type be
completely defined, whereas all the other type
composition constructs (arrays, records, access-to-T, protected, ...)
can use private types or other not-yet-completely-defined
types.  This is the problem, and I don't see any good
solution other than allowing a generic to take a
not-yet-completely-defined type as an actual.

I don't see any fundamental problem with the static elaboration
order checks, since we are only deferring the body elaboration
to later within the same list of declarations, something
that the static elaboration order stuff already has to deal
with for local subprograms, local packages, etc.

It might make sense so as to protect privacy and order of elaboration
of instance bodies, to say that if one generic instance
has its body deferred to a "general" freezing point, then
so do all instance bodies that occur after the first
instance and prior to the next general freezing point, with
the original order of instances preserved when elaborating the
deferred bodies.  This would mean that the only calls that
reach a subprogram inside a generic instance between the
elaboration of its instance spec and its instance body would
be calls coming from code directly within the list of declarations
following the instantiation, further simplifying static
elaboration order checking.

>>What Bob needs is a partial view of a generic to go along with partially
>>viewed type.

I doubt it. Bob really wants the flexibility afforded to
private types now, namely that you can define one in terms
of another before either is fully defined.  I don't see how
you can do this with a "partial view."  Presumably you are
only making incomplete types visible in this partial view.
He typically buries the level of indirection inside the
generic for something like an extensible sequence, so
he wants to be able to put the sequence-of-T as a direct
component of T.

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

From: Tucker Taft
Sent: Monday, September 29, 2003  9:51 PM

P.S. This whole thing need only apply to package instances.  There seems
to be no reason to ever defer elaboration of a subprogram
instance body, since the elaboration doesn't do anything other
than set an elaboration bit, and if you want to declare an operation
on a private type and implement it using a subprogram instance, we can
already do that using renaming-as-body, avoiding any freezing
problems.  So this would mean that the existing situation where
a subprogram instance doesn't need an elaboration bit would
be preserved.  (There is no equivalent to renaming as body
for a package, because the package spec might be defining
types which are referenced immediately after the package spec.)

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

From: Pascal Leroy
Sent: Tuesday, September 30, 2003   3:25 AM

Tuck said:

> As I said Bob Duff I believe expressed support for this
> proposal. Is that enough, or do you want another?  If so,
> anyone out there interested in making generics more useful by
> relaxing these freezing rules?  Please speak up now!

Bob replied:

> Yes, I do support this.

and Gary added:

> I'll add my second.  I agree this is worth discussing.

Amazing!  It's the first time we resurrect one of these AC thingies.
Good to see that we get to exercise all the paths in the ARG procedures.

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

From: Robert I. Eachus
Sent: Tuesday, September 30, 2003  11:31 AM

> Anyway, it occurs to me that the main problem could be solved with an
> "incomplete" instantiation. To show Tucker's original example...

I like it.  We could even fold it into the solution to the limited with
proposal to the extent possible to eliminate the number of new concepts that a
user must learn.  Hmmm.  Maybe not.  The real problem is that for "ordinary"
generics, instantiation of the body occurs at the same place as the
declaration.  The need is for separating the two.  So what we really want is
something that allows the generic instantiation of the body to be deferred,
while leaving the spec visible.

Something like:

package Foo will be new G(T); -- better syntax invited.

The instance of course can't be called (or objects in the generic used) before
the "full" declaration, but since that must be later in the same declarative
part  or in the private part, it isn't a problem for users of a package.

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

From: John-Pierre Rosen
Sent: Wednesday, October  1, 2003  2:08 AM

Since you ask for it (inventing syntax is such a fun):

package Foo is separate new G(T);

and at the place where you want the body to appear:

separate package body Foo;

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

From: Randy Brukardt
Sent: Tuesday, September 30, 2003  4:05 PM

Tucker wrote:

> This seems to be exactly the kind of discussion that
> is best done in person at an ARG meeting.  I hope we
> will get some time on the agenda for this (but I
> recognize we should put a time limit on the discussion).

Why? It's impossible in a very limited time to make technical arguments
coherently. They make much more sense when written. The usual reason that
face-to-face is preferred is to save time (writing responses takes a long
time). And, of course, we get to hear from more people.

But this is a black and white issue: either you believe in the corruption of
the Ada model that is espoused here, or you don't. (Which partially depends
on whether you believe in the program design which is suggested.)

...
> I don't see this as a visibility problem.  It is
> exactly a freezing problem.  The freezing rules for generic
> instantiation effectively require that the actual type be
> completely defined, whereas all the other type
> composition constructs (arrays, records, access-to-T, protected, ...)
> can use private types or other not-yet-completely-defined
> types.  This is the problem, and I don't see any good
> solution other than allowing a generic to take a
> not-yet-completely-defined type as an actual.

OK, here's the problem. A generic is not "a type composition construct".
It's a program unit. It needs to be used like one, not like "a type
composition construct".

> >>What Bob needs is a partial view of a generic to go along with partially
> >>viewed type.
>
> I doubt it. Bob really wants the flexibility afforded to
> private types now, namely that you can define one in terms
> of another before either is fully defined.  I don't see how
> you can do this with a "partial view."  Presumably you are
> only making incomplete types visible in this partial view.
> He typically buries the level of indirection inside the
> generic for something like an extensible sequence, so
> he wants to be able to put the sequence-of-T as a direct
> component of T.

The example in the AI was of a signature package. I can imagine wanting to
include a signature in the defining package - that potentially is worth
solving.

However, declaring a visible container depending on a private type is a
design fault (even if it was allowed). Putting a hodge-podge of
abstractions, and especially a mixture of visibility on those abstractions,
into a package spec is a no-no. I don't see why we should been over
backwards to support bad designs.

Note that there is no problem with the example you suggest here: just put it
into the private part where it belongs. If the user needs access to a
sequence-of-T, let them declare their own. (Better yet, don't use containers
for simple tasks better programmed directly -- but that's a separate issue.)

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

From: Tucker Taft
Sent: Tuesday, September 30, 2003  6:49 PM

>>This seems to be exactly the kind of discussion that
>>is best done in person at an ARG meeting.  I hope we
>>will get some time on the agenda for this (but I
>>recognize we should put a time limit on the discussion).
>
> Why? It's impossible in a very limited time to make technical arguments
> coherently. They make much more sense when written. The usual reason that
> face-to-face is preferred is to save time (writing responses takes a long
> time). And, of course, we get to hear from more people.

I don't have the same experience, but I realize people
differ on these things.  I find a "white-board" discussion
is sometimes the only way to come to a decision about
a tough technical issue, whereas going back and forth
with e-mail just ends up in circular arguments.  But,
this is definitely one of those things where your mileage
may vary.

> OK, here's the problem. A generic is not "a type composition construct".
> It's a program unit. It needs to be used like one, not like "a type
> composition construct".

I don't follow you here.  A generic is the only user-defined
type composition construct Ada provides.  It is implemented
as a program unit, but that doesn't make it less of a
type composition construct.

> Note that there is no problem with the example you suggest here: just put it
> into the private part where it belongs. If the user needs access to a
> sequence-of-T, let them declare their own. (Better yet, don't use containers
> for simple tasks better programmed directly -- but that's a separate issue.

But the problem is you can't even put it in the private
part, because it would still need to precede the full
definition of the private type.

I really don't see what is your objection to this
kind of use of generics.  You in particular have indicated
a desire to minimize visible use of access types,
so having a seq-of-T generic that has a hidden level
of indirection seems perfectly reasonable, so they
can be conveniently assigned and passed around.
Once you have one of these, it seems reasonable to
want to put one of these inside T itself (e.g.,
T is representing an expression, and it has
an operator and a seq-of-expressions for operands).

And note that if you made the package non-generic,
what was being done would be perfectly legal.
That seems like the most obvious flaw.  You can
do something useful in a sub-package, but you try
to bundle it up into a reusable generic, and the
freezing rules prevent you from using an instance
of the generic in place of the original sub-package.
Nothing else is different between the instance
and the sub-package, only the freezing rules.  So
to me that points the finger at the freezing rules.

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

From: Randy Brukardt
Sent: Tuesday, September 30, 2003  7:19 PM

> I don't have the same experience, but I realize people
> differ on these things.  I find a "white-board" discussion
> is sometimes the only way to come to a decision about
> a tough technical issue, whereas going back and forth
> with e-mail just ends up in circular arguments.  But,
> this is definitely one of those things where your mileage
> may vary.

Well, as recorder, I tend to have to spend my time writing down the
discussion, not participating in it. Moreover, I often record points that I
intend to make, and sometimes I never even manage to make them.

...
>> Note that there is no problem with the example you suggest here: just put it
>> into the private part where it belongs. If the user needs access to a
>> sequence-of-T, let them declare their own.
>
> But the problem is you can't even put it in the private
> part, because it would still need to precede the full
> definition of the private type.

Right because you want indirection without indirection. You can't get there
from here, and that is as it should be.

> I really don't see what is your objection to this
> kind of use of generics.  You in particular have indicated
> a desire to minimize visible use of access types,
> so having a seq-of-T generic that has a hidden level
> of indirection seems perfectly reasonable, so they
> can be conveniently assigned and passed around.
> Once you have one of these, it seems reasonable to
> want to put one of these inside T itself (e.g.,
> T is representing an expression, and it has
> an operator and a seq-of-expressions for operands).

I'm for minimizing *visible* use of access types. It's silly to even try to
eliminate them inside the implementation: indirection is a basic concept
that is needed everywhere. You can't put a T inside of a T without some
indirection, and its silly to try to hide it.

This seems to be the "recursive type" discussion that's going on on
Ada-Comment. Some people (Ed in particular) claims its impossible to do it
without indirection, and I tend to agree. And forcing compilers off the
market simply so you can avoid writing indirection when you clearly need it
seems completely pointless.

> And note that if you made the package non-generic,
> what was being done would be perfectly legal.
> That seems like the most obvious flaw.  You can
> do something useful in a sub-package, but you try
> to bundle it up into a reusable generic, and the
> freezing rules prevent you from using an instance
> of the generic in place of the original sub-package.
> Nothing else is different between the instance
> and the sub-package, only the freezing rules.  So
> to me that points the finger at the freezing rules.

True, but that's a bug in Ada 83. Being allowed to do that causes all of the
bizarre "when is a operation visible" rules, which have kept the ARG busy
for years. No one really knows what will happen when you do that; you just
hope that your compiler accepts it. It would have been much better if such
nested constructs were illegal. Making more of them legal is a lousy idea.

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

From: Ed Schonberg
Sent: Tuesday, September 30, 2003  7:19 PM

Like other implementors, I somewhat dread touching the freezing machinery,
because a number of invariants in the communication between front-end and
back-end will be affected. At the programming level, this suggestion also
seems to complicate things. Are entities within the instance usable before
its freezing point? Types that depend on the unfrozen actuals obviously
are not usable yet, but what about other entities? Do we need special rules
for this, or is the instance simply not usable at all until its freezing
point? This is not the case for nested packages, so this another epsilon
in complication.

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

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

I'm not sure I follow you.  The normal freezing rules still apply
to the entities inside the formal instance.  If you use them
in certain ways, then you freeze them, and it would be an error
if the entity was not yet completely defined.  This is exactly
like the entities in a nested package.  In fact, the whole goal
is that rather than freezing the actuals up front, instead, freezing
happens just as it would for the equivalent non-generic nested
package spec.  Remember that for a nested package, the end of
the package is *not* a "general" freezing point.  The general
freezing point happens at the end of the enclosing library unit
or declarative part, or at a non-instance body.  The proposal
is to defer the elaboration of the instance bodies to this same
"general" freezing point, and do freezing within the instance
spec just as it would be done for the equivalent non-generic
package spec.

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

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

> I'm not sure I follow you.  The normal freezing rules still apply
> to the entities inside the formal instance.

There is nothing 'normal' about an instance.

Entities in it are already frozen (because the entities were frozen when the
template was compiled - else code couldn't have been generated for it), and
they're duplicated that way. Freezing of entities in a generic template can
have no effect: they've already been frozen long before, and the representation
cannot change.

Moreover, we have to generate code for the actual parameters at the point of
the instantiation. We could delay that until the freezing point, but then we'd
have to delay the elaboration of the spec until that point -- it uses the same
actual parameters, thunks, and the like. And it certainly wouldn't be practical
to try to generate two sets of thunks and parameters, one for the spec and one
for the body. (Besides it not being possible in general.)

Beyond that, you're suggesting that everyone add a lot of code to detect
'early' freezing of (parts of) a generic instance. In particular, you would now
have to be able to trace freezing through generic formals -- which is clearly
new.

Moreover, a lot of the likely uses of the instantiation will raise
Program_Error. That doesn't seem too useful. I'd rather see an explicit
'partial' instantiation that allows signatures and incomplete types (solving
the real problems), and doesn't add a lot of new ways to raise Program_Error.

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

From: Tucker Taft
Sent: Wednesday, October  1, 2003  1:27 AM

Clearly you have special concerns due to
your "universal" generic sharing.  However, you
are using the term "freezing" in a way that doesn't
really correspond to its meaning in the manual.
It doesn't make sense to talk about freezing a type
when the template is compiled, if the type depends
fundamentally on properties of some actual type parameter
(e.g., an array type whose component type is a formal
type).  I can believe you "solidify" as much as you
can when you compile the generic template, but that
isn't the same as the RM meaning of "freeze."

Perhaps we need a specific example.
Here is a generic sequences package:

     generic
         type Elem is private;
     package Seqs is
         type Seq is private;
         procedure Append(S : in out Seq; E : in Elem);
         function Len(S : Seq) return Natural;
         function Nth(S : Seq; I : Positive) return Elem;
     private
         type Elem_Array is array(Positive range <>) of Elem;
         type Elem_Array_Ptr is access Elem_Array;

         type Seq is record
             Len : Natural := 0;
             Data : Elem_Array_Ptr := null;
         end record;
     end Seqs;

Here is an "Expression" type which consists of an operator
and zero-or-more operands, represented as a sequence.  This
would work fine if we hand-instantiated the Seqs package:

      type Expr is private;
      package Expr_Seqs is new Seqs(Expr); -- Freezing rule violation
      type Expr_Seq is new Expr_Seqs.Seq;
      function New_Expr(Op : Op_Enum; Opnds: Expr_Seq) return Expr;
    ...
    private
      type Expr is record
          Op : Op_Enum := No_Op;
          Operands : Expr_Seq;
      end record;

The above seems perfectly reasonable, except for the
fact that the freezing rules disallow it. I don't see
any "Program_Error"s cropping up.  These are all just
type declarations that build on one another.

If we ignore the current freezing rules for a minute,
can you identify the problems you would have generating
code for the above, presuming shared generics?  That
would be interesting to understand.

Here is a possible intermediate approach:

   When something declared in an instance needs to be frozen, then the
   entire instance spec is frozen, including all the generic actuals;
   the instance body, if any, is elaborated immediately after
   freezing the instance spec.

This means that at the point of instantiation, the spec
is elaborated, but no further layout decisions are
made.  At the point when any entity declared in the instance is
frozen due to an external "freezing" reference, the
layout decisions are made, 'Size is computed for everything,
and the body is elaborated.  This would mean the thunks,
etc., would be provided to the code that computes 'size, etc.
and then elaborates the body.  The elaboration of the instance
spec could presumably be done inline, since you have the
template spec available at the instantiation point.  Alternatively,
you could have a separate out-of-line procedure that
did just the spec elaboration.

If the elaboration of the instance spec itself caused
freezing of a generic actual that was not
completely defined, then the generic would clearly
not allow instantiation with that actual being
incompletely defined (just like the current rule).

For the above example, the elaboration clearly doesn't
freeze the generic actual type, so it could be instantiated
with an incompletely defined type.  Any "freezing" reference to
an entity exported by the instance would trigger freezing
of the entire instance.  So for example, a call on any subprogram
exported by the instance, or the declaration of an object
of a type exported by the instance, would trigger freezing
of the instance and its actuals, as well as the elaboration
of the instance body.

So if Seqs is instantiated with an incompletely defined
type (like Expr), then the freezing of the instance spec and
the elaboration of the instance body would be deferred until
the type Seq, or one of the subprograms Append, Len, or Nth is frozen,
or until a general freezing point, whichever comes first.

I believe this approach would also handle the problem
of Program_Errors due to access before elaboration,
since any use of the instance that would check an
elaboration bit would also cause freezing (and hence
body elaboration).  This would also preserve the existing
situation with respect to elaboration of generics,
where calls from outside the instance could never result
in the failure of an elaboration check.

I think this is nearly equivalent to the "partial" instantiation
you suggested.  Essentially the only thing that happens at
the point of the instantiation is the elaboration of the spec.
At a somewhat later point, freezing and body elaboration happen,
which is really the interesting stuff as far as your shared
generic implementation is concerned, it seems.

In other words, every instantiation with an incompletely-defined
actual type is considered a "partial" instantiation.  The
real "action" happens later, at the freezing point for some
entity exported by the generic.

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

From: Pascal Leroy
Sent: Wednesday, October  1, 2003  2:47 AM

I have some sympathy for solving that problem, as it occurs quite often
in practice.  However, I think that the approach of doing open-heart
surgery on the freezing rules and the elaboration model is misguided.

Couldn't we solve the problem with a categorization pragma?  Assume that
we add a library unit pragma Well_Behaved (better name welcome) that may
be applied to a generic and says "I promise that each and every instance
of this generic will be preelaborable" (the actual rules may need to be
a bit more complicated, but that's a first approximation).  This pragma
would prevent certain constructs inside the generic spec or body, like
declaring objects or having a statement part in the package body.

A generic with pragma Well_Behaved could then be instantiated with a
private type.  It would not cause freezing of that private type, and its
elaboration would be effectively a no-op, so I suppose that it wouldn't
be too hard for compilers to implement.

I believe that this pragma could actually be applied to many existing
generics without any code change (the containers being a good example).
And evidently it could be applied to signature packages.  So at the
expense of some generality, we would be able to solve the problem
without major changes to the language.

One advantage of this pragma is that it would document the fact that the
generic is specifically designed to be used with private types (i.e. for
type composition).

Does that make any sense?

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

From: Tucker Taft
Sent: Wednesday, October  1, 2003  7:50 AM

> ...
> I have some sympathy for solving that problem, as it occurs quite often
> in practice.

Given that we agree about this, it sounds like we might
have a chance of solving this problem.

> ... However, I think that the approach of doing open-heart
> surgery on the freezing rules and the elaboration model is misguided.

I prefer brain surgery ;-).

Actually, I felt my "intermediate" proposal, where spec elaboration
happens at the instantiation, and then the instance freezing
and body elaboration happen at a later point seems fairly
elegant and not particularly hard to implement.  It is clearly
upward compatible, since it is only used when one of the
actuals is not-completely-defined.  Also, it does exactly
what is needed, namely deferring freezing, but without creating
any "exposure" due to deferred elaboration of the body, because
that still happens prior to any possible external call.

> Couldn't we solve the problem with a categorization pragma?  Assume that
> we add a library unit pragma Well_Behaved (better name welcome) that may
> be applied to a generic and says "I promise that each and every instance
> of this generic will be preelaborable" (the actual rules may need to be
> a bit more complicated, but that's a first approximation).

Note that this is pretty much what the existing paragraph 10.2.1(10) is
all about.

> ... This pragma
> would prevent certain constructs inside the generic spec or body, like
> declaring objects or having a statement part in the package body.
>
> A generic with pragma Well_Behaved could then be instantiated with a
> private type.  It would not cause freezing of that private type, and its
> elaboration would be effectively a no-op, so I suppose that it wouldn't
> be too hard for compilers to implement.

I wouldn't mind a pragma-based solution, but it seems
more work to define a new pragma, and brings up the various
nasties associated with the preelaboration pragma, in particular
the requirement that the entire semantic closure be preelaborable.

On the other hand, if we could piggy back completely on the
existing Preelaborate pragma, and say that a generic that
is declared within a preelaborated library unit can be instantiated
with a not-yet-completely-defined type, that would be a fairly
economical solution.  However, I don't see it really simplifying
the implementation significantly, since you still have to notice
when a type exported by an instance of such a generic is frozen,
and at that point make sure the generic actual type is
completely defined.  It seems more flexible to just say that
any old generic works this way, and the freezing and body
elaboration happen at the freezing point rather than at the
instantiation point if there is a not-yet-completely-defined
actual.

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

From: Pascal Leroy
Sent: Wednesday, October  1, 2003  8:04 AM

> Actually, I felt my "intermediate" proposal, where spec elaboration
> happens at the instantiation, and then the instance freezing
> and body elaboration happen at a later point seems fairly
> elegant and not particularly hard to implement.

One thing I don't like here is the fact that the place where body
elaboration happens is "floating": add a new object declaration, and it
may cause instance freezing because it contains (at 17 levels deep) a
type exported by the instance; and now body elaboration happens earlier,
and that may change the dynamic effect of the program.  This is very
different from the current state of affairs, where it is very clear in
the program text where elaboration happens (and that doesn't change at
the drop of a hat).

I would be much happier if there were no need to elaborate the body at
all.

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

From: Ed Schonberg
Sent: Wednesday, October  1, 2003  8:13 AM

The simplest might be to treat instances exactly like nested packages then.
A use of an entity exported by the instance might freeze the entity without
freezing the instance itself, and the instance is frozen at the end of the
current declarative part, etc. There will be some instantiation errors when
the instance declares an object of an unfrozen type, but this will be no
different from the current treatment.

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

From: Tucker Taft
Sent: Wednesday, October  1, 2003  7:50 AM

Alternatively, we stick with a model where the freezing and
body elaboration happen at the same time (which I think
helps the shared generic implementation, and keeps the
nice elab-check-minimization properties), but say the
freezing and elab happen at the next "general" freezing
point, and until that point, all of the entities of
the instance are considered not-yet-completely-defined,
making an attempt at freezing an error.

In other words, if you can't freeze all the generic
actuals at the point of instantiation, then all freezing
and body elab is deferred until a well-defined point (the
next general freezing point), and any need for freezing before
that is an error.

I think this would address Pascal's concern by having a stable
well-defined point for body elab, minimize acc-before-elab problems
by keeping freezing and body elab together, and allow generics
to be used with not-completely-defined types.

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

From: Ed Schonberg
Sent: Wednesday, October  1, 2003  9:11 AM

I don't know about the difficulties presented by shared generics, but
if Randy allows me to ignore them for a microsecond, It seems to me
that the simplest is to erase 13.14 (5) from the RM, and be done. An
instance is a nested package, it may include unfrozen entities, these
might get frozen at some subsequent point of use, the instance itself
is frozen (together with the entities it contains) at the next general
freezing point. The instance body is conceptually at the freeze point.

Microsecond over, Randy will explain why this does not work with shared
generics.

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

From: Pascal Leroy
Sent: Wednesday, October  1, 2003  9:12 AM

> The simplest might be to treat instances exactly like nested
> packages then.

The analogy between nested packages and instances doesn't work well
because an instance has, in addition to a specification, a body, which
also occurs at the place of the instantiation (12.3(12)).  That's why I
don't take at face value Tuck's argument that "it's legal if you expand
by hand": you can only expand by hand the specification, not the body.

The nasty case of course is when a construct in the instance body causes
freezing: surely we do not want the legality of an instantiation to
depend on the contents of the body.

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

From: Robert I. Eachus
Sent: Wednesday, October  1, 2003  2:37 PM


I am definitely in the "don't mess with the freezing rules" camp.  But I have
been doing some more thinking about how this problem mixes with the issues
being addressed by "limited with."  Let me repeat Tucker's example to keep it
handy:

> Perhaps we need a specific example.
> Here is a generic sequences package:
>
>      generic
>          type Elem is private;
>      package Seqs is
>          type Seq is private;
>          procedure Append(S : in out Seq; E : in Elem);
>          function Len(S : Seq) return Natural;
>          function Nth(S : Seq; I : Positive) return Elem;
>      private
>          type Elem_Array is array(Positive range <>) of Elem;
>          type Elem_Array_Ptr is access Elem_Array;
>
>          type Seq is record
>              Len : Natural := 0;
>              Data : Elem_Array_Ptr := null;
>          end record;
>      end Seqs;
>
> Here is an "Expression" type which consists of an operator
> and zero-or-more operands, represented as a sequence.  This
> would work fine if we hand-instantiated the Seqs package:
>
>       type Expr is private;
>       package Expr_Seqs is new Seqs(Expr); -- Freezing rule violation
>       type Expr_Seq is new Expr_Seqs.Seq;
>       function New_Expr(Op : Op_Enum; Opnds: Expr_Seq) return Expr;
>     ...
>     private
>       type Expr is record
>           Op : Op_Enum := No_Op;
>           Operands : Expr_Seq;
>       end record;

And let me propose a strawman for the explicit notation camp:

       type Expr is private;
       package Expr_Seqs is limited new Seqs(Expr);
            -- types are visible as private here.
       subtype Expr_Seq is new Expr_Seqs.Seq;
            -- can't derive from a private type
       function New_Expr(Op : Op_Enum; Opnds: Expr_Seq) return Expr;
       procedure Append(S : in out Expr_Seq; E : in Expr);
       function Len(S : Expr_Seq) return Natural;
       function Nth(S : Expr_Seq; I : Positive) return Expr;
     ...
     private
       type Expr is record
           Op : Op_Enum := No_Op;
           Operands : Expr_Seq;
       end record;

       package Expr_Seqs is new Seqs(Expr)
       -- effectively the completion of the limited new

       -- use renamings to complete the declarations of Append, Len, and Nth

Does it mean more work than Tucker's magic wand?  Yes.  It also means that if a
programmer goofs, he is likely to get meaningful error messages instead of
prematurely freezing a type, then getting a boatload of secondary messages.

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

From: Randy Brukardt
Sent: Monday, October 27, 2003 11:11 PM

We never really finished the discussion of this AI; the discussion stopped when
everyone left for Canada.

Let me try to show some examples of why I don't like this proposal.

Let's say we have a generic array hashing package:

    generic
        type Elem is private;
        with function Code (I : Elem) return Natural;
        type Elem_Array is array (Positive range <>) of Elem;
    package Array_Hashing is
        function Hash (Arr : Elem_Array) return Natural;
    end Array_Hashing;

    package body Array_Hashing is
        type Nat_Mod is mod Natural'Last;
        function Hash (Arr : Elem_Array) return Natural is
            Res : Nat_Mod := 1;
        begin
            for I in Arr'range loop
               Res := Res*3 + Nat_Mod(Code(Arr(I)));
            end loop;
            return Natural(Res);
        end Hash;
    end Array_Hashing;

Now, let's look at a possible use in existing code:

    with Array_Hashing;
    package User is
        type Something is range 0 .. 200;
        function Count (A : Something) return Natural;
        type Array_of_Something is array (Positive range <>) of Something;
        package My_Hash is new Array_Hashing (Something, Count, Array_of_Something);
        NULL_HASH : constant Natural := My_Hash.Hash ((1..0 => 0));
        ....
    end User;

This code is legal and does not raise any exceptions (yes, I tried it on both
Janus/Ada and GNAT). Yes, I know this example is a bit contrived, but I wanted
something simple to discuss.

Now, consider that an Ada 200Y user decides to take advantage of Tucker's great
new feature and recreate package User with Something as a private type:

    package User is
        type Something is private;
        function Count (A : Something) return Natural;
        type Array_of_Something is array (Positive range <>) of Something;
        package My_Hash is new Array_Hashing (Something, Count, Array_of_Something);
        NULL_HASH : constant Natural := My_Hash.Hash ((1..0 => 0));
    private
        type Something is range 0 .. 200;
    end User;

With Tucker's original proposal, this is illegal, because the call to
My_Hash.Hash will try to freeze Something. This is ugly (because the error
message is likely to be totally mysterious), but reasonable (the user is
depending on a private type).

OK, so the user realizes their mistake, and makes NULL_HASH a deferred
constant. Their next program looks like:

    package User is
        type Something is private;
        function Count (A : Something) return Natural;
        type Array_of_Something is array (Positive range <>) of Something;
        package My_Hash is new Array_Hashing (Something, Count, Array_of_Something);
        NULL_HASH : constant Natural;
    private
        type Something is range 0 .. 200;
        NULL_HASH : constant Natural := My_Hash.Hash ((1..0 => 0));
    end User;

This compiles fine (Something is frozen by the call, which is OK, because it is
fully defined). But when they run it, it mysteriously raises Program_Error! The
reason is that the instance body of My_Hash doesn't elaborate until the end of
the package, so it isn't elaborated yet. Worse, there is absolutely nothing
that the user can do about it - there is no way to move the body to a point
before the deferred constant.

Tucker later modified his proposal - indeed, he did it 3 or 4 times. I don't
have a good way to refer to the different modifications, so I'm just going to
ignore the middle ones (which weren't liked much anyway).

The last modification was that the freezing and elaboration would happen at the
next "general" freezing point. (He didn't define "general", but I presume he
means body-or-end-of-scope.) In that modification, the program above would be
illegal (the instantiation is not frozen when the subprogram is called). That's
better than Program_Error, but the problem is that there isn't any way to make
the program legal.

A couple of other approaches were also tossed out. Pascal suggested a
categorization pragma. It seems to me that this generic would be able to be
used with such a pragma; the elaboration is trivial. However, that doesn't
help: we still have the access-before-elaboration and/or access-before-freezing
problem to resolve. (We can't elaborate, even logically, a body before
everything it depends on is frozen.)

Finally, there is the explicit approach that I originally proposed and later
expanded upon by Robert Eachus. Presuming semantics similar to limited with, we
would have:

    package User is
        type Something is private;
        function Count (A : Something) return Natural;
        type Array_of_Something is array (Positive range <>) of Something;
        package My_Hash is limited new Array_Hashing (Something, Count,
            Array_of_Something);
        NULL_HASH : constant Natural;
    private
        type Something is range 0 .. 200;
        package My_Hash is new Array_Hashing (Something, Count,
            Array_of_Something);
        NULL_HASH : constant Natural := My_Hash.Hash ((1..0 => 0));
    end User;

Which works fine: the user can place the elaboration/freezing of the unit
precisely where it needs to be.

The one difference that I suggested from what Robert suggested is that outside
of the package spec, there is no problem seeing the full view of the limited
instantiation. It only needs to be limited within the package specification
itself. That would mean that it would be legal to use this as a formal package
and to call routines within it from outside of the package (if you didn't want
to do that, why would you make it visible in the first place?). So it would be
defined as restrictions that applied only when you were in the scope of the
limited view but not the scope of the full view.

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

From: Tucker Taft
Sent: Tuesday, October 28, 2003  7:43 AM

Randy Brukardt wrote:
...
> Let me try to show some examples of why I don't like this proposal...

In your example, you are trying to take advantage of the
one "feature" the current model of generics gives you over
hand instantiation, namely immediate elaboration of the body,
whereas the purpose of this proposal is to postpone immediate
elaboration of the body to increase the flexibility of instantiations,
and bring them closer to the flexibility of hand-instantiation.

In my own view, there is such a huge advantage of being able
to match the flexibility of hand instantiation, that I am
willing to forego the advantage of early body elaboration
when the types are not completely defined at the point of
instantiation.

You have suggested we could get the best of both worlds by
allowing the programmer to explicitly separate the instantiation
into two pieces, using the word "limited" for the spec instantiation
and the existing syntax for body instantiation.
My concern is that this is adding syntax, making yet another
overloading of limited (with rather different meaning, since
I don't think we really have a "limited view" of the package),
and giving instantiations yet another special property that distinguishes
them from non-instantiations.

Do you feel this is really that important?  My own view is that
the "real" problem is that deferred constants can't be deferred
to the body.  This would allow "normal" functions to be used
in defining them, whereas now only instantiations can be
used in this way.  I would rather "fix" the real problem,
rather than going to the trouble of making instantiations different
from non-instantiations in yet another way.

One of the few times I "miss" C is when Ada forces me to
put the initialization of large constant tables into a spec,
or change them into variables, or use some other trick,
whereas in C I could have easily placed the initialization
in the equivalent of a body.

-Tuck

P.S. For the record, here is a copy of the suggested semantics
given in my 10/1/03 e-mail message:

> Alternatively, we stick with a model where the freezing and
> body elaboration happen at the same time (which I think
> helps the shared generic implementation, and keeps the
> nice elab-check-minimization properties), but say the
> freezing and elab happen at the next "general" freezing
> point, and until that point, all of the entities of
> the instance are considered not-yet-completely-defined,
> making an attempt at freezing an error.
>
> In other words, if you can't freeze all the generic
> actuals at the point of instantiation, then all freezing
> and body elab is deferred until a well-defined point (the
> next general freezing point), and any need for freezing before
> that is an error.
>
> I think this would address Pascal's concern by having a stable
> well-defined point for body elab, minimize acc-before-elab problems
> by keeping freezing and body elab together, and allow generics
> to be used with not-completely-defined types.

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

From: Robert A. Duff
Sent: Tuesday, October 28, 2003  8:01 AM

What do you mean by "hand instantiation"?

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

From: Tucker Taft
Sent: Tuesday, October 28, 2003  8:33 AM

By "hand instantiation" I mean copying the generic
unit, removing the word "generic", and editing it to
use the actual parameters.   A goal of this AI is
to allow a "real" generic instantiation to do the
same kinds of things a "hand" instantiation can do,
in particular, define some kind of set or sequence or
other collection where the component type is a private
type.

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

From: Randy Brukardt
Sent: Tuesday, October 28, 2003  3:35 PM

> Do you feel this is really that important?

Yes, actually. If we were defining "Bob 2005", this wouldn't be an important
property. But we have 25 years of Ada history here. At least some users expect
the body to elaborate at the point of the instantiation.

Indeed, it is a well-known "technique" to use a generic unit to get a body to
elaborate "early" in a spec. at a point where a body would be illegal for a
non-generic unit. I've used that technique at least once, and I know of other
users that have used it.

If we didn't have the history and compatibility problems, we could change the
meaning generally. But I object to the meaning changing silently in a few
cases, with no indication in the source that something unusual is going on. Ada
is all about readability, and we don't want minor changes far away in the
source code to make a significant semantic difference. We call that a
Beaujolais effect for visibility. I especially don't like the fact that such a
change can still produce a legal program, but one that could raise
Program_Error.

And I don't like the fact that you have to look at *all* of the generic
parameters before you can figure out anything about the instantiation -- as
what you do is totally different in the two cases. If you have a large number
of generic parameters, which is not uncommon, it can be hard to tell - as a
reader and as an implementation what semantics is in effect.

As far as the syntax goes, feel free to suggest something else. But it is very
close to a "limited view", with the exception that you are allowed to use the
full view outside of the package. Since for "limited with", you get no view
outside the package (as there is no visible declaration), I don't think that
this would not be a major confusion.

It's a "limited view" because the types declared in the spec have to be
incomplete. In particular, you must not be allowed to declare a component of
them. If you could, the legality of the generic instantiation would depend on
not only the formals and the contents of the generic spec, but also on how the
generic and its formals are used. There a few such cases in Ada currently
(mostly involving private types), and they are a disaster. I'm strongly opposed
to adding more, especially for a marginal feature such as this.

Instantiations *are* different. Their dynamic properties are (or at least can
be) completely different than the equivalent hand-instantiated code. That will
remain true unless we decide to eliminate the possibility of sharing, and until
we change the elaboration of ALL generic bodies to happen at freezing point
where bodies would normally be allowed. I do not expect that to happen.

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

From: Robert A Duff
Sent: Tuesday, October 28, 2003  4:23 PM

A question for Randy: In your proposal, are you allowed to put the body
of the instance in the body of the containing package (when the spec of
the instance is in the spec of the containing package)?
Why/why not?

I kind of like Randy's idea.  Tucker says it makes instantiations
different from non-instantiations "in yet another way".  But a normal
package is split syntactically into two pieces, and the programmer gets
to choose where they get elaborated (more or less).  It seems like
Randy's idea makes instantiations *more* like regular packages, in this
regard.

Randy wrote:

> Indeed, it is a well-known "technique" to use a generic unit to get a body
                              ^^^^^^^^^
> to elaborate "early" in a spec. at a point where a body would be illegal for
> a non-generic unit.

You misspelled "ugly hack".  ;-)

> I especially don't like the fact that such
> a change can still produce a legal program, but one that could raise
> Program_Error.

I agree, but exceptions during elaboration are *almost* as nice as
compile-time errors, because they usually happen every time you run the
program.  They will be noticed by any programmer who does the most
minimal amount of testing, and fixed right away, which is cheap -- the
same advantage as compile-time errors.  Almost.

> I'm
> strongly opposed to adding more, especially for a marginal feature such as
> this.

I don't have a strong feeling about how to fix the problem.
But I don't consider it a marginal feature.  I find the lack of the
ability to instantiate a generic on a private type to be huge pain.

Why doesn't Ada have (say) growable arrays built in?  Well, you can
build them yourself, using a generic.  I can say:

    type T is private;
    type T_Sequence is array(...) of T;

But I cannot say:

    type T is private;
    package T_Sequences is new Growable_Array(T); -- Illegal.

at least not without standing on my ear and spitting nickles.

That's a nasty inconsistency.

Note that in my code, quite often T contains a sequence-of-T's as a
subcomponent.

Tucker wrote:

> Do you feel this is really that important?  My own view is that
> the "real" problem is that deferred constants can't be deferred
> to the body.

Yes, we should fix that, too.

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

From: Randy Brukardt
Sent: Tuesday, October 28, 2003  5:05 PM

Bob said:

> A question for Randy: In your proposal, are you allowed to put the body
> of the instance in the body of the containing package (when the spec of
> the instance is in the spec of the containing package)?
> Why/why not?

I hadn't thought about that much, although I was certain someone would ask the
question. :-)

I think it would probably need the same rule as incomplete types, such that the
completion needs to be in the spec if it is in the visible part, and could be
in the body otherwise. For the same reasons that is true. (Is that vague
enough.) But it would depend solely on which operations are allowed on the
"limited view" (see below).

...
> Why doesn't Ada have (say) growable arrays built in?  Well, you can
> build them yourself, using a generic.  I can say:
>
>     type T is private;
>     type T_Sequence is array(...) of T;
>
> But I cannot say:
>
>     type T is private;
>     package T_Sequences is new Growable_Array(T); -- Illegal.
>
> at least not without standing on my ear and spitting nickles.
>
> That's a nasty inconsistency.
>
> Note that in my code, quite often T contains a sequence-of-T's as a
> subcomponent.

Right, and that's the reason that the view has to be limited. Because if it is
not, the legality of the uses of the view depends on the implementation
(specifically the private part).

For instance, if (bounded) growable array looks like:

    generic
        type T is private;
    package Bounded_Growable_Array is
        type Growable_Array (Limit : Natural) is private;
    private
        ...
    end Bounded_Growable_Array;

whether
    type T is private;
    package T_Sequences is limited new Bounded_Growable_Array (T);
    ...
    type T is record
        Foo : T_Sequences.Growable_Array (100); -- Illegal in my proposal.
    end record;
is implementable depends on the contents of the private part of
Bounded_Growable_Array.
OTOH,
    type T is record
        Foo : access T_Sequences.Growable_Array :=
		new T_Sequences.Growable_Array(100);
    end record;
is always OK.

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

From: Randy Brukardt
Sent: Tuesday, October 28, 2003  5:17 PM

To expand on that a bit:

This is important because the generic a separate unit, that may not even
belong to the current programmer. That is very different than the case with
the types declared directly at this point.

If we allowed what is allowed for private types here, then changing the
private part (implementation) of a generic could make code very far away
from the generic illegal. Imagine an implementor changing the
Ada.Components.Bounded_Array code from an indirect to direct
implementation -- imagine all of the code that would suddenly break. A
freezing-type rule here would mean that it would not be safe to change the
implementation of a generic unit for fear that it would break existing code.
That would be a bad policy for Ada to adopt.

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

From: Tucker Taft
Sent: Monday, June 7, 2004  10:42 PM

Here is an update to AI-359-01. [This is version /03 of the AI - ED]
It provides a pragma Deferred_Freezing_Point which when applied
to a generic_instantiation, defers both freezing
and instance body elaboration to the point of the
pragma.  We had talked about deferring to the first point
when some freezing reference was made to an entity
defined by the instantiation, but that made the
point where instance body elaboration occurred
subject to the mysteries of the freezing rules.
So I made the freezing and elaboration happen
at the point of the pragma itself, which seems
to eliminate that problem, and should provide
sufficient flexibility.  In general, one would
place the pragma after all the types
passed as actuals to the instantiation had been
completely defined.

The net effect is that the freezing rules affect
legality, but do not affect the dynamic semantics,
since the instance body elaboration is based on the
location of the pragma, rather than on when the
first freezing reference to the instance occurs.

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

From: Pascal Leroy
Sent: Tuesday, June 8, 2004  1:55 AM

Is this pragma only allowed for instantiations which are packages?  I can't
seem to find this in the AI.  Obviously for an instantiation which is a
subprogram the local_name is not enough to denote a particular
instantiation.

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

From: Tucker Taft
Sent: Thursday, June 10, 2004  7:44 AM

I mispelled "precede" at least once in this AI.
Sorry about that.

As far as applying to subprogram instantiations
that are overloaded, I would go the route of pragma
Inline and say it applies to all overloadings
that are instances, and their freezing and
body elab all take place at the point of
the pragma.  This seems like a highly unlikely
case to begin with, so almost anything that
doesn't muck up the basic capability would
seem OK.

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

From: Tucker Taft
Sent: Thursday, June 10, 2004  10:02 AM

Actually, perhaps this is a waste of energy.
Since we already support renaming as body as
a way to postpone a subprogram instantiation
into a private part, we might as well make this
pragma be available only for package instantiations.

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

From: Robert A. Duff
Sent: Sunday, June 20, 2004  1:16 PM

First, I am very much in favor of fixing this problem.  The inability to
instantiate generics where I want to has caused a lot of damage to the
structure of my programs (making types public when they should be
private, introducing gratuitous indirections of various sorts, and
various other workarounds).

----------------

I don't much like the name of this pragma.  Freezing is an arcane
obscurity that shouldn't be mentioned in polite company.  That's why
it's banished to chap 13.

Why don't we look at it this way: the *body* of the instance is what
causes freezing of all manner of things (because we don't want to look
inside it -- the contract model).  By default, the body of the instance
occurs at the same place as the spec -- namely, at the place of the
instantiation.  This new pragma provides a way to put the body of the
instance somewhere else.

So we could call it pragma Place_Instance_Body_Here, or just
Place_Instance_Body, or just Instance_Body, or something along those
lines.

----------------

The reason we need a pragma (or the proposed restriction of alternative
AI-359-02) is, I presume, upward compatibility.  We don't want to move
the place where the elaboration happens.

Is that really a serious concern?  I find it difficult to imagine a
generic that would care so precisely when its instance's body gets
elaborated.

I can live with the pragma (especially if it has a better name), but I
think I'd prefer just fixing the problem directly.

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

From: Randy Brukardt
Sent: Wednesday, June 23, 2004  9:58 PM

> Is that really a serious concern?  I find it difficult to imagine a
> generic that would care so precisely when its instance's body gets
> elaborated.

There are at least two problems:

-- All Ada compilers use a single elaboration check at the point of the
instantiation, rather than checking the items individually. Changing that
would be rather pervasive, and would have a (small) performance
incompatibility.

-- Doing complex things at elaboration-time *appears* useful. So journeyman
Ada programmers try to do clever things there. I've seen quite a bit of such
code, and written some myself. Once you get to be an expert, you realize
that such things are fragile, and stop doing them -- replacing them with
explicit calls or controlled type initialization. But there is an awful lot
of fragile code out there that does weird things at elaboration time. (I
used to have lots of code that read from a configuration file - the order
mattered a lot there.) Breaking that by changing elaboration ordering is the
worst kind of incompatibility (it happens silently with no indication of any
kind).

Since it is the worst kind of incompatibility, we need to avoid it unless
we're certain that it doesn't happen in practice. But I think it's likely
that it does occasionally happen in practice.

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


Questions? Ask the ACAA Technical Agent