Version 1.2 of ais/ai-50217.txt

Unformatted version of ais/ai-50217.txt version 1.2
Other versions for file ais/ai-50217.txt

!standard 10.01.02 (03)          03-02-04 AI95-00217-06/01
!standard 10.01.02 (04)
!standard 10.01.02 (08)
!standard J.10 (00)
!class amendment 03-02-04
!status work item 03-02-04
!status received 03-02-04
!priority Medium
!difficulty Hard
!subject Limited With Clauses
!summary
A new kind of with_clause is proposed.
!problem
Ada allows mutually recursive types to be declared only if they are all declared within the same library unit. This can force all of the major data structures of a program into a single library unit, which is clearly undesirable in some situations.
The goal of the proposed feature is to allow mutual recursion among type declared in separate packages (types containing (pointers to) each other as components, and primitive operations with (pointers to) each other as parameters), and to do this in a way that doesn't place any undue restrictions on the programmer.
For mutually recursive types, it is valuable if subprogram parameters may be of the type itself, rather than only an access to the type. However, for most types, it may be necessary to see the full definition to know how parameters of the type are passed. However, because tagged types are always passed by reference, there is no implementation difficulty in allowing them to be used as parameters even when the full definition of the type is not available. Hence, it makes sense to relax the rule for using incomplete types that are known to be tagged, to allow them as formal and actual parameters, since from a code generation point of view they are essentially equivalent to access parameters.
!proposal
A new kind of with_clause is added, of the form "limited with <library package names>;". This is called a "limited_with_clause". The old fashioned kind is called an "unlimited_with_clause".
There are two key features of a limited_with_clause such as "limited with X;":
- It introduces no semantic dependence on X (and hence no
elaboration dependence on X). The implementation may choose to elaborate X either before or after the current unit. [In the wording below, I ended up saying it introduces a semantic dependence on the limited view of X.]
- Instead of providing visibility to the named packages and their
content, it provides visiblility only to the package names, any packages nested within them, and a view of each type therein. The type view is incomplete, implying all the usual restrictions on incomplete types. If the type is tagged, the view is tagged incomplete.
Thus, cyclic chains of with_clauses are allowed, so long as the chain is broken by at least one limited_with_clause.
!wording
10.1.2(4) says:
with_clause ::= with library_unit_name {, library_unit_name};
Change to:
with_clause ::= limited_with_clause | unlimited_with_clause limited_with_clause ::= limited with library_unit_name {, library_unit_name}; unlimited_with_clause ::= with library_unit_name {, library_unit_name};
Add a legality rule:
A library_item mentioned in a limited_with_clause shall be a package_declaration[, not a subprogram_declaration, generic_declaration, or generic_instantiation].
Discussion of existing wording:
It is natural to think of a with_clause as causing a library unit to become visible. However, that's not what the RM says. Instead, the RM says that lack of a with_clause causes a library unit not to be visible. That's kind of odd, but to understand the wording below, you have to understand the existing model in this language-lawyerly fashion.
The current RM defines the notion of an "environment declarative_part". During compilation, this fanciful declarative_part is imagined to contain all the library_items of interest. The order of these library_items is such that there are no forward semantic dependences. With_clauses introduce semantic dependences, so they control the order. Semantic dependences are also caused by child-parent and body-spec relationships. Subunits are imagined to be "inlined" at their stub point, so subunits can be ignored for visibility purposes.
The visibility rules for library units come from treating the environment declarative_part in the usual way, with one difference: In a normal declarative_part, you can see everything that comes before, and nothing that comes after (with a bunch of hiding rules thrown in as well). In the environment declarative_part, however, you can see things that come before, but only if mentioned in a with_clause.
Similar oddities occur in the definition of "immediate scope" and "scope".
I'm not sure what's the best way to introduce limited_with_clauses into this model. One idea is to say that "limited with X" means you can see X even if X occurs later in the environment declarative_part. This would introduce (conceptual) forward references, something never seen in Ada before. Another idea is to say that there is an implicit declaration of a "limited view of package X" (occurring before any "limited with X"'s). The normal declaration of X then appears later in its usual place. Either way, "limited with X" introduces a very limited view of X that only includes nested packages and incomplete types. I think I'll try the latter idea, even though I hate to introduce yet more magical implicit junk.
Here goes:
10.1.4(1-3) says:
1 Each compilation unit submitted to the compiler is compiled in the context of an environment declarative_part (or simply, an environment), which is a conceptual declarative_part that forms the outermost declarative region of the context of any compilation. At run time, an environment forms the declarative_part of the body of the environment task of a partition (see 10.2, ``Program Execution'').
2 The declarative_items of the environment are library_items appearing in an order such that there are no forward semantic dependences. Each included subunit occurs in place of the corresponding stub. The visibility rules apply as if the environment were the outermost declarative region, except that with_- clauses are needed to make declarations of library units visible (see 10.1.2).
3 The mechanisms for creating an environment and for adding and replacing compilation units within an environment are implementation defined.
Add some text somewhere in there:
In addition, for each library_package_declaration in the environment, there is a conceptual declaration of a "limited view" of that library package. [Note: This animal is sort of what folks have been calling a "package abstract", except that it is automatically generated (or imagined) by the compiler.]
The limited view of a package contains:
- A declaration of the limited view of each nested package.
- For each type_declaration in the visible part, an
incomplete_type_declaration with the same name, whose completion is the type_declaration. If the type_declaration is tagged, then the incomplete_type_declaration is tagged incomplete.
The limited view of a package has the same identifier as the package_declaration.
There is no syntax for declaring limited package views, because they are always implicit. Nonetheless, the declaration of a limited package view is considered to be a library_item.
A name mentioned in a limited_with_clause denotes the limited view declaration. I guess we need to make sure the definition of "mentioned" is appropriate: for a limited_with_clause, we are "mentioning" the limited view.
A library_package_declaration is considered to be the completion of its limited view declaration.
10.1(26) says:
26 A library_item depends semantically upon its parent declaration. A subunit depends semantically upon its parent body. A library_unit_body depends semantically upon the corresponding library_unit_declaration, if any. A compilation unit depends semantically upon each library_item mentioned in a with_clause of the compilation unit. In addition, if a given compilation unit contains an attribute_reference of a type defined in another compilation unit, then the given compilation unit depends semantically upon the other compilation unit. The semantic dependence relationship is transitive.
Add some new cases:
- A limited library package view declaration depends semantically
upon the parent limited package view declaration.
- A library package declaration depends semantically upon its
limited view.
- The part about with_clauses should work unchanged: for a
limited_with_clause, the dependence is upon the limited view; for an unlimited_with_clause, the dependence is upon the package_decl.
There are various places in Chapter 8 that refer to "the declaration of a library unit", presuming that there's only one. We need to change the wording, since library packages now have two declarations: the limited view, plus the normal package_decl. With minor wording changes, most of it should work OK, because:
A library_package_decl is the completion of its limited view, and therefore hides it.
The incomplete types in the limited view are completed by the regular types. This introduces the possibility of completing an incomplete type with a private type, but I think all the proposals are going to have a similar problem (if it's a problem).
!discussion
To review this proposal, one should search the RM for the following terms, to see if they still make sense:
- with_clause - semantic dependence, depend[s] semantically
Also study chaps 8 and 10.
----
Here's one possible implementation method: Whenever the compiler sees "limited with X", it reads the source file containing X, parses it, and collects a list of nested package and type names, and whether they're tagged. It is important that this process can be done without doing any "serious" semantic analysis, and without looking at anything imported by X. The compiler then constructs the limited view of X.
Luckily, taggedness of types can be detected syntactically.
At any place where a package_decl is visible, the compiler needs to be sure the limited view of it is hidden.
The compiler can avoid reconstructing the limited view of X, if it likes, if it knows the source has not changed.
For compilers that save program library information on disk, I presume the limited view would have to be saved on disk in a similar manner. In Rational terms, this means each source file has (up to) two compilation artifacts.
I presume all compilers have some subroutine to check whether two types are the same type (in case of incomplete types and the like). And/or a subroutine to determine which view of a type should be used. These subroutines need to be changed to deal with this new flavor of incomplete type. But I would expect that to be fairly localized; the calls to those subroutines should still work.
----
We do not allow a limited_with_clause to mention a procedure, because that would allow calling a procedure before its spec is elaborated. The language has no mechanism to deal with that. Similarly, we do not allow a limited_with_clause to mention a generic, because that would allow instantiating a generic before its spec is elaborated.
We do not allow a limited_with_clause to mention a generic instantiation, because that would require all kinds of semantic analysis stuff, such as visibility.
----
A package could say "limited with A.B.C;" and then its child could say "with A.B.C;". Now we're within the scope of the real things, so the limited views would be hidden.
Similarly, if P says "limited with P.C", we're within the scope of the real P, so the limited view of it is hidden. We have limited access to C.
----
Consider:
package A is type T is range 1..10; end A;
with A; use A; package B is X, Y: T; end B;
limited with A; with B; use B; package C is X := Y; end C;
OK, assignment statements are syntactically illegal in a package spec. But think of some other thing that depends on the type. Perhaps, "Flag: Boolean := X in 1..10;".
Is this legal? It would be odd if adding "limited with A;" reduced ones capabilities.
!example
!ACATS test
!appendix

From: Pascal Leroy
Sent: Monday, February  3, 2003  5:19 AM

A while back, Dan wrote, as a side comment: "Well, the nicest solution for the
user is just to allow specs to with each other, as other languages do."  This
got me thinking, as this is an approach that we have discussed briefly once or
twice, but writers of library-based compilers (like me) started to yell and
scream that it would be impossible to implement.  Well, I am starting to think
that all approaches considered so far are nearly impossible to implement, and
furthermore are quite hard to use.  So I started to investigate a solution where
units are allowed to with each others.  Here are my half-baked ideas:

1 - A new form of context clause is added:

    weak [private] with library_unit_name {, library_unit_name}

Weak with clauses don't have to form an acyclic graph.

2 - A compilation unit is added to the environment in two phases.  During the
first phase we say that the unit is "superficially added"; during the second
phase that it is "fully added" (better terminology welcome).  How this is
achieved is not specified by the language.

3 - To superficially add a unit to the environment, there is no requirement that
any other unit already exists in the environment.  (Not even its parent, heck,
not even Standard!)

4 - When a unit is superficially added to the environment, the only names that
can be referenced from outside the unit are those of packages (both the unit
itself and nested packages) and of types.  Moreover, every type is at this point
considered to be an incomplete type, and subject to the restrictions mentioned
in 3.10.1; the taggedness of the types is visible.  (The alert reader may notice
some similarity between a unit superficially entered into the environment and a
package abstract.)

5 - To fully add a unit to the environment, all of the units upon which it
depends semantically must already have been fully entered into the environment.
Furthermore, all of the units that it names in a weak with clause must already
have been superficially entered into the environment.  The only names that can
be referenced from such units are those of types and packages, and types are
considered incomplete.

6 - To fully add a unit to the environment, this unit must be made of the same
lexical elements (except for comments) as when it was superficially added to the
environment.

7 - The celebrated textbook example found in the AI is then simply written as
follows:

    weak with Departments;
    package Employees is
        type Dept_Ptr is access all Departments.Department'Class;
        type Employee is tagged private;
        procedure Assign_Employee(E : in out Employee;
                                  D : in out Departments.Department);
        ...
        function Current_Department(D : in Employee) return Dept_Ptr;
    end Employees;

    weak with Employees;
    package Departments is
        type Emp_Ptr is access all Employees.Employee'Class;
        type Department is tagged private;
        procedure Choose_Manager(D : in out Department;
                                 Manager : in out Employees.Employee);
        ...
    end Departments;

8 - Marketing hype a la Tucker:

    - No three-part types
    - No new compilation units
    - No new declarations
    - No changes to the visibility rules
    - Little new syntax
    - Support for circularities involving types in nested packages

I am no expert in source-based compilers, but I believe that this model should
be quite close to what GNAT currently does for "with type".  Superficially
adding a unit to the environment would merely mean that the source file exists,
and the "weak with" would give the compiler permission to peak at it.

For library-based compilers this proposal adds some complexity, but not
necessarily more than those proposals which change the visibility rules or
otherwise break the invariants upon which the compilers depend.  In the case of
our implementation a tree in the library can be in two states: parsed (as
produced by the parser, no names resolved) or analyzed (with all the decorations
produced by the semantic analyzer, names resolved, etc.).  These states would
correspond directly to a unit superficially/fully entered in the environment.
We would need to beef up the parsed representation a bit to add (1) tables
listing the types and package names, and (2) a hash code representing the
sequence of tokens in the unit (to perform the check of #6 above).  Intense
hand-waving here, as I have not done anything resembling a serious design.

Feel free to explain why this is a dumb idea...

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

From: Arnaud Charlet
Sent: Monday, February  3, 2003  5:35 AM

I certainly like Pascal's proposal (weak with) very much. It is the only
proposal (apart from the old with type that had its bunch of issues) so far
that looks appealing to me. It addresses well the issue of interfacing
with other languages and from a user point of view, it is very close to an
ideal solution.

As Robert said, we don't have any real technical constraint in GNAT to
implement any of the proposed solution. This one would probably be
close to the current with type implementation.

I am not sure the wording about adding a unit in the environment, and
requiring compilers to have two (or more for that matter) phases should be
stated as such, but I guess this can probably be sorted out without too much
difficulty if this proposal gets some more supporters.

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

From: Robert Dewar
Sent: Monday, February  3, 2003  6:39 AM

Pascal's proposal for weak with is the first one I have seen since WITH TYPE
that I like, and in fact I like it better than WITH TYPE (I am not taling
deep semantics here, just a surface impression). It also seems to satisfy
the requirement for easy generation of equivalent Ada from e.g. Java stuff.

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

From: Robert Dewar
Sent: Monday, February  3, 2003  6:42 AM

By the way I don't like "weak" very much because it has the wrong
connotations to be. The issue is that this with is preliminary or
limited (so if you are in a mood to reuse keywords.

limited with x;

is more like it ...

A more radical proposal is that all WITH's that meet the criteria are
weak -- could that work?

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

From: Pascal Leroy
Sent: Monday, February  3, 2003  6:51 AM

I am not wedded to a particular syntax.  If we think that the idea is worth
pursuing, we can probably come up with an acceptable syntax (we don't lack
creativity in the area of syntax).

> A more radical proposal is that all WITH's that meet the criteria are
> weak -- could that work?

From a methodological standpoint, I'd say that introducing a circularity should
be the result of a conscious decision, not something that happens by chance.
So the user should have to make it explicit that a circularity is what she
wants--by typing "weak", "limited" or whatever.

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

From: Robert Dewar
Sent: Monday, February  3, 2003  7:03 AM

I am not so sure I agree with this. At one level of abstraction you WITH
something because you need something in it, and it is not really your
business if it needs something in yourself.

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

From: Jean-Pierre Rosen
Sent: Monday, February  3, 2003  7:02 AM

> limited with x;
>
> is more like it ...
>
Or even
   with X abstract;

;-)

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

From: Robert A. Duff
Sent: Monday, February  3, 2003  8:18 AM

> A while back, Dan wrote, as a side comment: "Well, the nicest solution for the
> user is just to allow specs to with each other, as other languages
> do."

Dan was exactly correct.  That's why I can't get too excited about
Tucker's aesthetic gut feelings about the various other proposals --
they are *all* less aesthetically pleasing than the "right" solution.

>...This
> got me thinking, as this is an approach that we have discussed briefly once or
> twice, but writers of library-based compilers (like me) started to yell and
> scream that it would be impossible to implement.  Well, I am starting to think
> that all approaches considered so far are nearly impossible to
>implement, ...

That seems exaggerated, ...

>...and
> furthermore are quite hard to use.  So I started to investigate a solution where
> units are allowed to with each others.  Here are my half-baked ideas:
>
> 1 - A new form of context clause is added:
>
>     weak [private] with library_unit_name {, library_unit_name}
>
> Weak with clauses don't have to form an acyclic graph.

This is the solution I chose for my from-scratch language design I do as
a hobby.  My syntax is "import X;" to import X and require X to be
elaborated earlier than this unit -- essentially the same as Ada's "with
X;" And "import forward X;" to import X and elaborate X *after* this
unit.  This is essentially the same as your "weak with X;".  ("import
forward" is like a forward reference.)

The only thing needed in the RM is the syntax for weak with's, make sure
the rule about no cycles applies only to cycles formed by strong with's.

All the stuff below is really a description of how the Rational compiler
would work (and others with a similar library model).  There's no need
for any of it in the RM.

(Aside: In my "hobbyist" language, I am able to achieve complete
*compile-time* checking of elaboration order problems, even in the case
of indirect/dispatching calls, with no sacrifice in expressive power.
I thought that was pretty cool.)

> 2 - A compilation unit is added to the environment in two phases.  During the
> first phase we say that the unit is "superficially added"; during the second
> phase that it is "fully added" (better terminology welcome).  How this is
> achieved is not specified by the language.
>
> 3 - To superficially add a unit to the environment, there is no requirement that
> any other unit already exists in the environment.  (Not even its parent, heck,
> not even Standard!)
>
> 4 - When a unit is superficially added to the environment, the only names that
> can be referenced from outside the unit are those of packages (both the unit
> itself and nested packages) and of types.  Moreover, every type is at this point
> considered to be an incomplete type, and subject to the restrictions mentioned
> in 3.10.1; the taggedness of the types is visible.  (The alert reader may notice
> some similarity between a unit superficially entered into the environment and a
> package abstract.)
>
> 5 - To fully add a unit to the environment, all of the units upon which it
> depends semantically must already have been fully entered into the environment.
> Furthermore, all of the units that it names in a weak with clause must already
> have been superficially entered into the environment.  The only names that can
> be referenced from such units are those of types and packages, and types are
> considered incomplete.
>
> 6 - To fully add a unit to the environment, this unit must be made of the same
> lexical elements (except for comments) as when it was superficially added to the
> environment.

Note in particular that this consistency check is an artifact of your
compilation model.  That is, your compiler is going to read the same
file twice.  But I can imagine compilers that will only read the source
once, so the "superficial" and "full" distinction is represented purely
in memory during compilation.

I think any consistency requirements are already covered by the RM's
words about not including two different versions of the same unit in a
partition.

> 7 - The celebrated textbook example found in the AI is then simply written as
> follows:
>
>     weak with Departments;
>     package Employees is
>         type Dept_Ptr is access all Departments.Department'Class;
>         type Employee is tagged private;
>         procedure Assign_Employee(E : in out Employee;
>                                   D : in out Departments.Department);
>         ...
>         function Current_Department(D : in Employee) return Dept_Ptr;
>     end Employees;
>
>     weak with Employees;
>     package Departments is
>         type Emp_Ptr is access all Employees.Employee'Class;
>         type Department is tagged private;
>         procedure Choose_Manager(D : in out Department;
>                                  Manager : in out Employees.Employee);
>         ...
>     end Departments;

Yes, although only one of the with's *needs* to be weak.  For symmetry,
they probably both should be weak.

> 8 - Marketing hype a la Tucker:
>
>     - No three-part types
>     - No new compilation units
>     - No new declarations
>     - No changes to the visibility rules
>     - Little new syntax
>     - Support for circularities involving types in nested packages
>
> I am no expert in source-based compilers, but I believe that this model should
> be quite close to what GNAT currently does for "with type".  Superficially
> adding a unit to the environment would merely mean that the source file exists,
> and the "weak with" would give the compiler permission to peak at it.
>
> For library-based compilers this proposal adds some complexity, but not
> necessarily more than those proposals which change the visibility rules or
> otherwise break the invariants upon which the compilers depend.  In the case of
> our implementation a tree in the library can be in two states: parsed (as
> produced by the parser, no names resolved) or analyzed (with all the decorations
> produced by the semantic analyzer, names resolved, etc.).  These states would
> correspond directly to a unit superficially/fully entered in the environment.
> We would need to beef up the parsed representation a bit to add (1) tables
> listing the types and package names, and (2) a hash code representing the
> sequence of tokens in the unit (to perform the check of #6 above).  Intense
> hand-waving here, as I have not done anything resembling a serious design.
>
> Feel free to explain why this is a dumb idea...

Because your evil twin was yelling and screaming about the
implementation difficulty of this idea...  ;-)

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

From: Tucker Taft
Sent: Monday, February  3, 2003  8:24 AM

I like this, although it is
more work for us than almost any
other proposal.  We do all semantics
on the fly, and don't have a mode that
parses without doing semantic analysis.

In general, I believe the "type C.T;" approach
solves the basic problem, and will be one of the
easiest to implement.

As far as syntax, I like the suggestion of using
"limited" as a prefix on the "with" clause,
and it goes awfully nicely with "private" ;-).

    [limited] [private] with library_item_name {, library_item_name};

It would be a major headache (dare I say "huge" headache?)
for us if the "limited"ness of the "with" were not explicit.

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

From: Robert A. Duff
Sent: Monday, February  3, 2003  8:34 AM

Robert wrote:

> A more radical proposal is that all WITH's that meet the criteria are
> weak -- could that work?

No, I don't think so.  The problem is that if there's a cycle of with's,
the compiler needs to choose *one* of them as "weak" to break the
cycle.  The one that is chosen affects the elaboration order.  Clearly,
the user needs a way to control this order, and "weak with" is that
feature.

In other words, how is the compiler to know *which* with's meet the
criteria?

I don't think the usual army of elaboration-control pragmas can help.
Pragma Elaborate(X) should probably be illegal for weak withs.
Anyway, it only goes one step, which as we know from Ada 83 is not good
enough.  Pragma Elaborate_All should ignore weak withs that are found in
the chain.  Otherwise, it can't work when used in part of the cycle.

Lots of other languages do as Robert suggests -- just allow cyclic
imports, with no indication of which ones are "weak".  The reason they
can get away with it, and we can't is:
Some such languages don't have run-time elaboration semantics,
so there's no issue.
Other such languages have botched the run-time elaboration semantics
so badly that this issue is in the noise.

> I am not so sure I agree with this. At one level of abstraction you WITH
> something because you need something in it, and it is not really your
> business if it needs something in yourself.

I agree with Pascal on this point.  It seems to me that a mutually
recursive design always requires *some* mutually recursive knowledge on
the part of the designer.  I don't see it happening by accident.  At
least not unless the packages involved are incoherent collections of
unrelated stuff.

Normal layered "with"s have the property that packages don't need to
know about their clients.  Mutual recursion seems rather different to
me: two packages need to know about each other -- they at least need to
know about each other's *existence*.  So I think weak with's deserve
their own syntax.

I guess I agree with Robert that "weak with" is not the best syntax,
but that's a minor detail.  Using "with" as a verb is already an
annoyance...  Oh, well.

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

From: Robert Dewar
Sent: Monday, February  3, 2003  8:43 AM

> No, I don't think so.  The problem is that if there's a cycle of with's,
> the compiler needs to choose *one* of them as "weak" to break the
> cycle.  The one that is chosen affects the elaboration order.  Clearly,
> the user needs a way to control this order, and "weak with" is that
> feature.
>
> In other words, how is the compiler to know *which* with's meet the
> criteria?

Yes, that makes sense, so that's not a possibility that is worth discussing.
I withdraw the solution.

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

From: Pascal Leroy
Sent: Monday, February  3, 2003  8:37 AM

> The only thing needed in the RM is the syntax for weak with's, make sure
> the rule about no cycles applies only to cycles formed by strong with's.
>
> All the stuff below is really a description of how the Rational compiler
> would work (and others with a similar library model).  There's no need
> for any of it in the RM.

That statement surprises me.  At the very least, it would seem that the RM would
need to specify what names/entities are available after a unit has been
superficially entered into the environment, and what are the properties of these
entities.  I am proposing that you can see only packages and types, and even so
the types would look like they are incomplete.  I can't see how you could deduce
this from the current RM.

PS: But then I know that Bob Duff, like the Pope, is infallible in matters of
language doctrine ;-)

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

From: Robert A. Duff
Sent: Monday, February  3, 2003  8:51 AM

Yes, you're right -- the RM needs to say *something*.  But it's not
much.  And I don't think it needs to define this concept of
"superficially in the environment" -- that concept belongs in the
Rational user's manual, I think.

The RM should say something about how if the only visibility to package
X is via "weak with", then uses of X are restricted along the lines you
say above.

More thought is needed, if we are to go this route.  The only real
problem I see is implementation difficulty.  I think all the language
rules can be worked out, but that will require some thought.
Interactions with elaboration-control pragmas.  What happens when you've
got both a weak and a strong with on the same thing?  Does "weak with
A.B.C;" import all three weakly?  Is there any concern that changing
"with" to "weak with", or vice versa, would introduce surprises into the
program?  I suspect all these questions have easy straightforward
answers.

> PS: But then I know that Bob Duff, like the Pope, is infallible in matters of
> language doctrine ;-)

;-)

No way!

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

From: Robert A. Duff
Sent: Monday, February  3, 2003  12:56 PM

In Pascal's proposal, a parent package P can weakly 'with' its children:
"weak with P.C;", if you want a mutually dependent pair of types,
one in P and one in P.C.  The spec of P will be elaborated before the
spec of P.C, as usual.

However, it is pointless for P.C to say "weak with P;", because the
child-to-parent dependency is necessarily strong (given that there's no
proposal to add syntax saying otherwise -- "weak children?").

No problems here.  I'm just making some observations.

I guess I favor Robert's proposed "limited with" syntax.

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

From: Robert Eachus
Sent: Monday, February  3, 2003  2:26 PM

So do I, and I am beginning to like this proposal.  As far as I can see
it is clearly better than package abstracts in two ways, there is no new
compilation unit type, and there is no problem of keeping the abstract
consistant with the package itself.

In other words from a theoretical point of view, the "limited with"
creates a package abstract and then withs it.  Since the completion of
the types and the subprograms (and parameters) get lost in the process,
there is no visible circularity.

But I think that there are two "gotchas" that have to be dealt with.
 The first is where you have a type declaration that depends on another
type:

type Color is (Red, Green, Blue);
type Properties(C: Color) is...;

Does the incomplete type for Properties include the discriminant?
 Choices are yes, and it is illegal to do anything with it, type Color
is not incomplete, and the type Properties has undefined discriminants.
(Others?)  I prefer the choice where only private, record and tagged
types are incomplete in the limited view.

This brings me directly to the second gotcha.  I think it is clear (at
least to me) that for this to be useful, the limited view of an access
type has to be an access type.  But there have to be limits on how this
access type can be used in the limited view.  (Translation, if you
create a type with a component of  (a limited view of) an access type,
then it is illegal to have a non-null default value for a component of
that type.  Other rules are possible.)

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

From: Robert A. Duff
Sent: Monday, February  3, 2003  2:26 PM

> This brings me directly to the second gotcha.  I think it is clear (at
> least to me) that for this to be useful, the limited view of an access
> type has to be an access type.  But there have to be limits on how this
> access type can be used in the limited view.

Can't we say that it's an access type whose designated type is
incomplete?  And then you can't do things like X.all.

>...  (Translation, if you
> create a type with a component of  (a limited view of) an access type,
> then it is illegal to have a non-null default value for a component of
> that type.  Other rules are possible.)

I think you can't have an allocator (as a default or anything else),
but I don't see why it couldn't be a function call that happens to
return the access type.  Perhaps an example?

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

From: Dan Eilers
Sent: Monday, February  3, 2003  3:04 PM

I like Pascal's "weak with" proposal a lot, although I'm not yet convinced
we need to syntactically distinguish "weak with" from normal withs.

I came to this conclusion by perhaps a slightly different route:

1) we have a bunch of proposed syntax extensions to solve the circular
   types problem, any of which are believed to be feasible to implement;

2) essentially none of the proposed syntax extensions have any dual-use
   benefit towards the solution of any other problem;

3) we envision a tool that can automatically translate circular specs
   written in other languages into the proposed new syntax;

4) a similar tool could also translate Ada that allowed circular with's
   into any of the proposed syntax extensions;

5) given such a tool, the users would happily write Ada with circular
   with's, and use the tool to create their package abstracts (in
   whatever syntax);

6) this could be made automatic.  When the compiler sees a "with" clause
   for a unit that has not been compiled, instead of giving an error, it
   can automatically invoke the translation tool to create the package
   abstract, and then compile the abstract before continuing to compile
   the original unit.

6) this can be further optimized if desired so the translation tool is
   integrated into the compiler, rather than needing to be a stand-alone
   tool.


Notes:

Recursive subprogram calls don't use different syntax than non-recursive
ones, so ideally recursive with's wouldn't either.

We need to address the elaboration order concerns, but presumably that
is more naturally done with elaboration pragmas than with different
syntax for "with" clauses.

I still think there is some possible dual-use synergy with the separate
private issue.  As Tuck corrected me, you can't just put the private
part in a separate file.  So instead of "private is separate" you would
have to say "the remainder of this package is separate".  A "with" clause
on such a package would continue to see both the root and the subunit.
A "with p'abstract" would see only the root.  I believe such new syntax
would at the same time solve the separate private issue and the circular
types issue, and perhaps also the separate constants issue.

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

From: Robert Dewar
Sent: Monday, February  3, 2003  4:07 PM

> 6) this could be made automatic.  When the compiler sees a "with" clause
>    for a unit that has not been compiled, instead of giving an error, it
>    can automatically invoke the translation tool to create the package
>    abstract, and then compile the abstract before continuing to compile
>    the original unit.

That won't do at all for a source based compilation system, there is no
concept of "a unit that has not been compiled". One of the critical
guarantees in GNAt is that order of compilation NEVER affects the results
and we would strenuously object to a change in semantics which would
destroy this guarantee. For one thing it means that the meaning of a
program is not fully conveyed by its sources, and that is pragmatically
and philsophically unacceptable.

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

From: Robert A. Duff
Sent: Monday, February  3, 2003  4:16 PM

Yes, I agree.

This makes me think that these tools that translate from other languages
(or create bindings thereto, I guess) ought to make sure that their
generated Ada code doesn't do anything at elab time -- at least in the
presence of cycles.  That seems achievable, and simpler than having user
options to specify elab order.

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

From: Robert Dewar
Sent: Monday, February  3, 2003  4:25 PM

Note that another objection is that this means that automated tools for
determining the compilation order would have to do semantic analysis.

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

From: Robert A. Duff
Sent: Monday, February  3, 2003  4:12 PM

Dan Eilers wrote:

>...
> 3) we envision a tool that can automatically translate circular specs
>    written in other languages into the proposed new syntax;

Right, but those languages typically don't have the same
elaboration-order issues that Ada does.

> 4) a similar tool could also translate Ada that allowed circular with's
>    into any of the proposed syntax extensions;

True, but...

> 5) given such a tool, the users would happily write Ada with circular
>    with's, and use the tool to create their package abstracts (in
>    whatever syntax);

I fear that such a tool would be inherently error prone.  Suppose we
have package specs A and B, which 'with' each other.  The tool would
have to choose which one to elaborate first, and it might guess wrong.
I think it's better to let the user specify the order via "limited
with".

Besides, now that I think about it, how would this tool work?  "Limited
with" implies a very restricted usage of the imported package.  The tool
would have to check that such restrictions are obeyed before turning a
"with" into a "limited with".

> 6) this could be made automatic.  When the compiler sees a "with" clause
>    for a unit that has not been compiled, instead of giving an error, it
>    can automatically invoke the translation tool to create the package
>    abstract, and then compile the abstract before continuing to compile
>    the original unit.

Users normally don't compile.  They run a build tool, which
automatically compiles everything that needs to be compiled, in some
order that is not easily known to the programmer.  It seems error-prone
to have the semantics depending on what happens to have already been
compiled.  That's true even if the user bypasses the build tool and
explicitly invokes the compiler in some order.

> 6) this can be further optimized if desired so the translation tool is
>    integrated into the compiler, rather than needing to be a stand-alone
>    tool.
>
>
> Notes:
>
> Recursive subprogram calls don't use different syntax than non-recursive
> ones, so ideally recursive with's wouldn't either.

True, but this case seems different to me.

If I see:

    with B;
    package A is ...

I currently know for certain that the spec of B is elaborated before A.
I don't have to go looking at B to see that that's true.  I don't think
we should break that property.

> We need to address the elaboration order concerns, but presumably that
> is more naturally done with elaboration pragmas than with different
> syntax for "with" clauses.

I don't think the existing pragmas can be used for this purpose.
Suppose the cycle is "A with's B with's C with's A".  Adding a pragma
Elaborate in A pointing to B doesn't work, because it's not transitive
to C.  Adding Elaborate_All doesn't work, because that would require A
to be elaborated before itself (because it's transitive).

So we would have to add a new pragma to explicitly break one link in the
cycle:

    with X; pragma Don't_Elaborate(X); -- ;-)

But that means precisely what "limited with X;" would mean.

Elaboration pragmas were a mistake in the first place.  They were added
late in the game to Ada 83 to solve the "Brosgol anomalies".  We added
more pragmas in Ada 95, and I'm afraid we botched the job -- we *still*
didn't solve the problems.  I suggest we avoid compounding the mistake
of using pragmas to control elaboration.

Furthermore, "limited with X" implies various restrictions on the use of
X -- you can only refer to types in X, and they are all incomplete (or
access to incomplete).  Pragmas shouldn't be in *that* business.

> I still think there is some possible dual-use synergy with the separate
> private issue.  As Tuck corrected me, you can't just put the private
> part in a separate file.  So instead of "private is separate" you would
> have to say "the remainder of this package is separate".  A "with" clause
> on such a package would continue to see both the root and the subunit.
> A "with p'abstract" would see only the root.  I believe such new syntax
> would at the same time solve the separate private issue and the circular
> types issue, and perhaps also the separate constants issue.

Perhaps, but I skept.  I'm having trouble seeing the separate-private
issue as anything more than a simple textual translation, with no
run-time consequences whatsoever.  I haven't seen a fully worked-out
proposal that solves both problems...

P.S. This discussion makes me think of another observation.  If a given
cycle contains two or more "limited with"s, then elaboration order will
presumably be implementation defined.  I suppose that's OK -- if you
care, you can break the cycle with just one "limited with", and use
normal "with" for the rest.

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

From: Dan Eilers
Sent: Monday, February  3, 2003  6:23 PM

Bob Duff wrote:

> Perhaps, but I skept.  I'm having trouble seeing the separate-private
> issue as anything more than a simple textual translation, with no
> run-time consequences whatsoever.  I haven't seen a fully worked-out
> proposal that solves both problems...

OK, here's the canonical example worked out:

-- Note: with'ing package P creates a semantic dependency on all subunits
-- such as P.Extension declared in P, and makes all declarations in
-- P.Extension visible as if they were declared directly in P.

-- with'ing P'abstract does not create a semantic dependency on subunits
-- such as P.Extension, and does not make p.Extension's declarations visible,
-- and does not allow declaring objects of types from P whose full type is
-- deferred to P.Extension.

    package Employees is
        type Employee is private;       -- completed in subunit
        type Emp_Ptr is access all Employee;
        package Extension is separate;
    end Employees;

    package Departments is
        type Department is private;
        type Dept_Ptr is access all Department;
        package Extension is separate;
    end Departments;

    with Departments'abstract;
    separate(Employees)
    package Extension is
        type Emp_Ptr is access all Employee;
        procedure Assign_Employee(E : access Employee;
                                  D : access Departments.Department);
        function Current_Department(D : access constant Employee) return
            Departments.Dept_Ptr;
    private
        type Employee is record
           dept: Departments.Dept_Ptr;
        end record;
    end Extension;

    with Employees'abstract;
    separate(Departments)
    package Extension is
        type Dept_Ptr is access all Department;
        procedure Choose_Manager(D : access Department;
                                 Manager : access Employees.Employee);
    private
        type Department is
           mgr: Employees.Emp_Ptr;
        end record;
    end Extension;


For the "private is separate" problem, instead of:

    package p is
      ...
    private is separate;
    end P;

you would have:

    package p is
      ...
    private
      package extension is separate;
    end;

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

From: Tucker Taft
Sent: Monday, February  3, 2003  4:27 PM

Robert A Duff wrote:
>
> > This brings me directly to the second gotcha.  I think it is clear (at
> > least to me) that for this to be useful, the limited view of an access
> > type has to be an access type.  But there have to be limits on how this
> > access type can be used in the limited view.
>
> Can't we say that it's an access type whose designated type is
> incomplete?  And then you can't do things like X.all.

You are starting to give me the "willies."
We will have to process rep-clauses if the access type
in a limited-with-ed package is not itself considered an
incomplete type.

If we want a non-incomplete access type, then I think we need to
declare it in a non-limited-withed place.  Hence,
this doesn't solve the problem associated with proliferation
of access type declarations.

And I do still like that "type C.T;" ;-).
(what a pain I can be...)

> >...  (Translation, if you
> > create a type with a component of  (a limited view of) an access type,
> > then it is illegal to have a non-null default value for a component of
> > that type.  Other rules are possible.)
>
> I think you can't have an allocator (as a default or anything else),
> but I don't see why it couldn't be a function call that happens to
> return the access type.  Perhaps an example?

Gack.  You are back to requiring that the limited-with-er chooses
a representation for a type that it isn't declaring.
In this era where we are on the cusp of moving from 32-bit pointers
to 64-bit pointers, I fear that rep clauses on access types may
be more common, rather than less.



By the way, I have discovered (remembered?) that we have a "loose parser"
built into our compiler.  It's job is to do lookahead where strict one-pass
semantic analysis is difficult.  For example, it scans bodies looking for
labels, and then inserts them at the "begin."  It scans generic formal
parts looking ahead until it finds out in what parent unit the generic
is declared, so it can interpret the names in the formal part properly.
In any case, this is a full, but very stupid and quite "lax" parser.
It could probably build up a tree of package and type names.  It could
certainly not make any semantic sense out of a rep clause, and since the
rep clause could use named numbers declared in other packages
(e.g. "for My_Acc_Type'Size use Targ_Dep.Size_Of_Access_Type;"),
it really couldn't be made to work.

So if we consider this limited-with approach, I think we want to make
minimal assumptions about syntactic processing, and zero assumptions
about representation processing, of the items in the limited-withed unit.

I also agree with Bob that the less said about the way the magic
happens the better.  Clearly there will be some kind of compilation
dependence on the source of the limited-withed unit, but the key point is there
is no semantic or elaboration dependence on the unit.  It seems fine if
the source of the unit is edited "after" something that mentioned it in a limited-with
clause is compiled, but before the unit itself is compiled.  But of course,
whenever the source is changed, everything that has a compilation-dependence
on it will probably need to be recompiled prior to linking.

The "trick" that makes it all possible is that the *fully compiled* representation of
both units in a cyclic dependence depend on the *source text* of both units.
The fully compiled representations don't both depend on the others fully compiled
representation.  Of course, there may be intermediate forms between source
text and fully compiled representations, but those don't need to be mentioned.
And the other important point is there is no transitive compilation dependence
if the unit mentioned in a limited-with clause itself depends on other
units, via withs or limited-withs.

Presumably, mutual inlining, if supported, requires similar tricks.

Other observations if we consider limited-with:

I suppose to be consistent, we should interpret "limited with P.Q.R;"
as equivalent to "limited with P, P.Q, P.Q.R;".  Also it seems clear
that limited with is ignored if there is a non-limited with for the
same unit.

Another important point is that if a unit is mentioned in a limited with
clause, it is a *post*-compilation rule that the unit be semantically
(or even syntactically) correct, as opposed to a legality rule.  When compiling
the unit having the limited with, the compiler may of course check some part of
this post-compilation rule early (provided of course it doesn't create an inappropriate
semantic dependence), but an ACATS test should not require that any particular
errors in the unit mentioned in the limited-with be identified at this time.
Certainly compilers will vary in how picky is the parser that they
use to implement limited with, especially given that Ada's "syntax"
rules slop over into semantic processing fairly frequently, due to the
lack of direct LALR(1) parsability.

The only requirement should be that *if* the unit is semantically correct,
then you can safely mention it in a limited-with.  But you cannot rely
on the compiler detecting all (or any) errors in a unit mentioned in a limited-with,
though it can be arbitrarily picky about it.  Eventually, prior to linking,
it will require that the unit exist and be semantically correct.  This
means that when a unit mentions a second unit in a limited-with
clause, the second unit is "needed by" the first unit, in the
sense of 10.2(2-6).

The above is related to 10.1.4(5) which says:

   When a compilation unit is compiled, all compilation units upon which
   it depends semantically shall already exist in the environment...

We could change this to "... upon which it depends semantically or
which it mentions in a limited_with_clause shall ..." and it would still be OK.

However, 10.1.4(6) which says:

   The implementation may require that a compilation unit be legal before
   inserting it into the environment.

is too strict.  It could be changed to:

   The implementation may require that a compilation unit satisfy the Syntax
   Rules before inserting it into the environment.  The implementation may
   require that a compilation unit be legal before allowing it to be mentioned
   in a with_clause other than a limited_with_clause.

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

From: Robert A Duff
Sent: Monday, February  3, 2003  5:17 PM

Tucker wrote:

> Robert A Duff wrote:
> > Can't we say that it's an access type whose designated type is
> > incomplete?  And then you can't do things like X.all.
>
> You are starting to give me the "willies."
> [...description of willies]

OK, I withdraw my suggestion.

But it seems like whatever solution to the mutual recursion problem we
choose, we really need to solve the problem of proliferation of silly
type conversions.  Otherwise, these proposals will be nearly unusable.

That is, there ought to be an *implicit* conversion between access types
in the "safe" cases.  I know we discussed that before.  Is it part of
any of these proposals, or is it a separate AI?  I seem to recall that
it might be wrapped up with other access-type issues, like allowing "not
null" constraints, and access-to-constant parameters.

> And I do still like that "type C.T;" ;-).
> (what a pain I can be...)

That's a fine solution, IMHO.  Your earlier e-mail about
mutually-recursive types being part of the same abstraction
makes me comfortable with the idea of tying the solution to
child packages.  (See, it *is* possible to convey sensible information
about purely aesthetic concerns.  ;-))

But I would choose the "limited with" idea, unless it is considered too
hard to implement.

> Presumably, mutual inlining, if supported, requires similar tricks.

And mutual generic instantiation?

> Other observations if we consider limited-with:
>
> I suppose to be consistent, we should interpret "limited with P.Q.R;"
> as equivalent to "limited with P, P.Q, P.Q.R;".  Also it seems clear
> that limited with is ignored if there is a non-limited with for the
> same unit.

Or any other "strong" dependence, such as child upon parent.

I guess "strong dependence", as I've been calling it, is exactly the
same notion as "semantic dependence".

> Another important point is that if a unit is mentioned in a limited with
> clause, it is a *post*-compilation rule that the unit be semantically
> (or even syntactically) correct, as opposed to a legality rule. ...

I don't see the need for any verbiage in the RM about this point.
And I don't see how this differs (in theory) from normal with_clauses.
(It might differ in practise, depending on how the compiler actually
works.)

I mean, if B says "with A;", there is currently no rule saying "A shall
be legal."  A compiler could properly say nothing more than "no errors
found in B", even if there are errors in A.  (That would be unfriendly,
IMHO.)  Of course, sometime before running the program, A must be
compiled, and the errors detected.

> ...This
> means that when a unit mentions a second unit in a limited-with
> clause, the second unit is "needed by" the first unit, in the
> sense of 10.2(2-6).

Yes, that's a key point.  It might be that the second unit is mentioned
*only* in limited_with_clauses, in which case there are no elaboration
dependendences upon it, but it still needs to be included in the
program, and elaborated at some point (possibly very late).

> The above is related to 10.1.4(5) which says:
>
>    When a compilation unit is compiled, all compilation units upon which
>    it depends semantically shall already exist in the environment...
>
> We could change this to "... upon which it depends semantically or
> which it mentions in a limited_with_clause shall ..." and it would still be OK.

I don't see any need to change this, given that "existing in the env" is
such a vague concept.  In GNAT, it just means the file is sitting there
on the disk.  In AdaMagic, it just means it is sitting there on the disk
and has been "registered".  Registration only does minimal syntax
checking.  In Rational's compiler, it means something more.

> However, 10.1.4(6) which says:
>
>    The implementation may require that a compilation unit be legal before
>    inserting it into the environment.
>
> is too strict.  It could be changed to:

I don't see why.  The GNAT model does not take advantage of this
permission.  The permission is there for the benefit of compilers like
Rational, where "inserting" involves running the compiler, and therefore
checking various rules.  I suppose AdaMagic takes partial advantage of
this permission, since insertion involves some small amount of syntax
checking.

But why should we care how much checking is done on B before "limited
with B" can be swallowed by the compiler?  If B is illegal, the compiler
can process "limited with B" if it likes, or (if it happens to notice
the illegality), it can complain that "limited with B" is nonsense.
It doesn't matter whether the illegality is syntactic or semantic.

>    The implementation may require that a compilation unit satisfy the Syntax
>    Rules before inserting it into the environment.  The implementation may
>    require that a compilation unit be legal before allowing it to be mentioned
>    in a with_clause other than a limited_with_clause.

I see no need for this added complexity.  And it could be a burden -- if
the compiler chooses to implement some simple Legality Rule in the
parser, that should be allowed.

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

From: Tucker Taft
Sent: Monday, February  3, 2003  6:20 PM

Robert A Duff wrote:
> Tucker wrote:
> ...

>>The above is related to 10.1.4(5) which says:
>>
>>   When a compilation unit is compiled, all compilation units upon which
>>   it depends semantically shall already exist in the environment...
>>
>>We could change this to "... upon which it depends semantically or
>>which it mentions in a limited_with_clause shall ..." and it would still be OK.
>
> I don't see any need to change this, given that "existing in the env" is
> such a vague concept.  In GNAT, it just means the file is sitting there
> on the disk.  In AdaMagic, it just means it is sitting there on the disk
> and has been "registered".  Registration only does minimal syntax
> checking.  In Rational's compiler, it means something more.

I think we need to say *something* about the requirements
on a unit mentioned in a limited-with clause.  Why not say
it must exist in the environment?  As you said, that is
pretty vague.  If there are no requirements on it, then
the implementation has no place to "hang" whatever requirements
it really does have.  It can't just invent requirements that
don't exist.  But it can say, "as far as our implementation
is concerned, that unit doesn't exist in the environment,
so we refuse to compile the limited-with for it."

>>However, 10.1.4(6) which says:
>>
>>   The implementation may require that a compilation unit be legal before
>>   inserting it into the environment.
>>
>>is too strict.  It could be changed to:
>
> I don't see why.

Again, we need a place for the implementation to "hang"
its requirements.  We have given them the first handle,
by allowing them to require existence.  Now we are
giving them the second handle, allowing them to impose
certain other requirements on the unit.  They *clearly*
can't impose complete legality requirements, if the
units are inserted one at a time, since there is no
order of insertion that would allow that.

> ...
> The GNAT model does not take advantage of this
> permission.  The permission is there for the benefit of compilers like
> Rational, where "inserting" involves running the compiler, and therefore
> checking various rules.  I suppose AdaMagic takes partial advantage of
> this permission, since insertion involves some small amount of syntax
> checking.
>
> But why should we care how much checking is done on B before "limited
> with B" can be swallowed by the compiler?  If B is illegal, the compiler
> can process "limited with B" if it likes, or (if it happens to notice
> the illegality), it can complain that "limited with B" is nonsense.
> It doesn't matter whether the illegality is syntactic or semantic.

You are missing the point.  I am simply *allowing* compilers
to impose requirements, but only up to a point.
Without such verbiage, I don't believe compilers would be
allowed to impose any requirements.

I suppose you could argue all of this verbiage is unnecessary
already, but since we felt the need to put it there in the
first place, and we presumably aren't going to take it out,
I believe it needs to be updated to *allow* compilers to impose
certain requirements on limited-with'ed units.  But I definitely
don't want to force them to impose these requirements
(and I don't think my suggested wording does that).

>>   The implementation may require that a compilation unit satisfy the Syntax
>>   Rules before inserting it into the environment.  The implementation may
>>   require that a compilation unit be legal before allowing it to be mentioned
>>   in a with_clause other than a limited_with_clause.
>
> I see no need for this added complexity.  And it could be a burden -- if
> the compiler chooses to implement some simple Legality Rule in the
> parser, that should be allowed.

I'm not sure what we are discussing any more, since you
don't seem to think that the unit even need to exist
in the environment to be mentioned in the limited-with
clause.  I think we need to *allow* implementations
to impose some requirements, and we might as well tie
it to the notion of "exist" in the environment.
*If* we tie it to that notion, we want to be sure that
limited-with can in fact be used.  That seems to mean
that you must be able to insert units into the environment
before they can be (fully) compiled, strictly for the purposes
of doing a "limited with" of them.

Why don't you suggest some wording so we actually
have an alternative to consider?

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

From: Robert A Duff
Sent: Monday, February  3, 2003  6:46 PM

Tucker wrote:

> I think we need to say *something* about the requirements
> on a unit mentioned in a limited-with clause.  Why not say
> it must exist in the environment?  As you said, that is
> pretty vague.  If there are no requirements on it, then
> the implementation has no place to "hang" whatever requirements
> it really does have.  It can't just invent requirements that
> don't exist.  But it can say, "as far as our implementation
> is concerned, that unit doesn't exist in the environment,
> so we refuse to compile the limited-with for it."

OK, you have convinced me on that point.  But as to 10.1.4(6):

> Again, we need a place for the implementation to "hang"
> its requirements.  We have given them the first handle,
> by allowing them to require existence.  Now we are
> giving them the second handle, allowing them to impose
> certain other requirements on the unit.

But 10.1.4(6) as is already gives sufficient permission.
Your proposed change gives *less* permission.
I claim there is no need to be more restrictive (on implementations),
and that being more restrictive requires more complex RM wording.

10.1.4(6) as is allows compilers to refuse to deal with "limited with X"
on an illegal X.  That is sufficient permission.  If X is in the env,
and is legal, the compiler must deal with "limited with X", because the
only permission to refuse to admit X into the env is if it's illegal.

>  They *clearly*
> can't impose complete legality requirements, if the
> units are inserted one at a time, since there is no
> order of insertion that would allow that.

There is no requirement that units are inserted one at a time (and in
fact they are not, in AdaMagic).  Hence, it is not "*clearly*" true that
they can't impose legality requirements.

Your wording for 10.1.4(6) does not allow a compiler to impose such
legality requirements.  I think that's overspecification, and could be
an implementation burden.  For example, it is reasonable to implement
some legality rules in the parser, and some compilers do that.
One might wish to run the parser on X when seeing "limited with X".
Your wording would disallow such an implementation.

Imagine a front end structured as three phases:

    1. Parse and build syntax tree.
    2. Walk tree, build symbol table nodes for explicit declarations,
       collect a list of all type decls, and check *some* legality
       rules.
    3. Overload resolution, and check the rest of the legality rules.

The "limited with" wants that list of type decls from phase 2.
But your wording seems to forbid phase 2 from printing any error
messages, because they're not syntax errors.

> I'm not sure what we are discussing any more, since you
> don't seem to think that the unit even need to exist
> in the environment to be mentioned in the limited-with
> clause.

I backed off on that part.

>...  I think we need to *allow* implementations
> to impose some requirements, and we might as well tie
> it to the notion of "exist" in the environment.
> *If* we tie it to that notion, we want to be sure that
> limited-with can in fact be used.  That seems to mean
> that you must be able to insert units into the environment
> before they can be (fully) compiled, strictly for the purposes
> of doing a "limited with" of them.
>
> Why don't you suggest some wording so we actually
> have an alternative to consider?

OK, I propose changing 10.1.4(5) in the way you suggested, and leaving
10.1.4(6) as is.

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

From: Tucker Taft
Sent: Monday, February  3, 2003  7:02 PM

Robert A Duff wrote:
 > ...
> OK, I propose changing 10.1.4(5) in the way you suggested, and leaving
> 10.1.4(6) as is.

I suppose there is an unwritten rule that there
must be some way for two packages with cylic
dependence to be inserted into the environment.
Clearly if the "insertion" process requires legality,
and you can only insert one unit at a time,
there is a problem.  Either the compiler
must allow simultaneous multiple-file insertion
(that sounds like it might be illegal in Nebraska ;-),
or it must allow units that are not yet demonstrably
legal to be inserted.

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

From: Robert A Duff
Sent: Monday, February  3, 2003  7:14 PM

Don't you think it's OK to leave that rule unwritten?
I mean if that's a hole, it's already a hole.

If I have some legal code, I must be able to insert it into the env
*somehow*, since there's no permission for the implementation to refuse
legal code.  The fact that the implementation is incapable of
determining whether it's legal seems irrelevant.  *I* know it's legal.

I think you're worrying overmuch about implementation issues (in
considering RM wording).  Legal code must be accepted.  Therefore, code
must be accepted if the compiler does not (yet) know whether it's legal.
The compiler can only refuse if it can *prove* it's illegal.  Make
sense?

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

From: Tucker Taft
Sent: Monday, February  3, 2003  8:33 PM

After thinking more about this, I think we need
to make some change to the wording of 10.1.4(6).  It really
doesn't make any sense if we change 10.1.4(5) as proposed.
We seem to agree that this sentence is primarily for
non-source-based compilers, where the only way to insert a
unit into the environment is by processing it in some way.
But it is exactly those compilers that will have to change to
support limited-with.  They *cannot* require legality of all
units inserted into the environment, if we have just
changed 10.1.4(5) to require that the unit mentioned in
a limited-with must already *be* in the environment.

I think we should say something closer to this:

   Before inserting a compilation unit into the environment,
   the implementation may require that it obey all the Syntax
   Rules, and any other rules that it can check given
   what other units already exist in the environment.

> ...
> I think you're worrying overmuch about implementation issues (in
> considering RM wording).  Legal code must be accepted.  Therefore, code
> must be accepted if the compiler does not (yet) know whether it's legal.
> The compiler can only refuse if it can *prove* it's illegal.  Make
> sense?

No. ;-).

I think either we should make 10.1.4(6) completely
implementation-defined, or replace it with something
that reflects the problem that non-source-based
compilers will have with limited withs, since we
know that 10.1.4(6) was crafted specifically for
their needs.

Remember, this is in the section called "implementation
permissions" so it is specifically talking to implementors,
not users, and it is specifically talking about implementation
issues, not abstract semantic issues.

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

From: Robert A Duff
Sent: Monday, February  3, 2003  7:06 PM

Pascal,

This "weak with" ("limited with") thing is your proposal, but if you
like, I might be willing to work out the RM wording tomorrow (Tuesday).
This idea seems to be gaining favor, and if it's to be chosen, I don't
really want that to take 17 more ARG meetings.  Producing wording now
might speed things up.

On the other hand, if folks think it's too hard to implement, please say
so before I waste my time.

Should I do it?

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  3:21 AM

It would be very useful if you did that.  You are much more competent than me at
crafting RM wording, and furthermore I don't think I would have time to do that
between now and the meeting.

Since there seems to be some interest for the idea, it would be good to have a
precise proposal on the table to discuss it.  Experience has proven that
discussing an AI without an initial write-up is sterile.

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

From: Randy Brukardt
Sent: Monday, February  3, 2003  8:35 PM

I'm not Pascal, but I'd suggest going ahead and writing up the wording. I'm
having a hard time getting a handle on this proposal, and I'm sure that
would help. OTOH, since it is a complete start from scratch, it's going to
take 17 meetings to get it right in any case. At most, you can reduce that
number by one. :-)

> On the other hand, if folks think it's too hard to implement, please say
> so before I waste my time.

At the moment, I think it's too hard to implement (given that type stubs are
expected to take 4 hours of work; this would be more like 4 weeks). But that
may be irrelevant. More important is that I don't see any way, within the
current design of Janus/Ada, to implement something like this.

The problem is with the obsoleteness check. Janus/Ada uses a serial number
created from the time stamp of the symbol table file created for a library
unit for checking. Also, the Janus/Ada compiler knows nothing about source
files other than the one it is compiling at the moment. (Other tools are a
bit smarter, but the compiler does not use that information itself.) It only
knows about symbol table files.

One presumes that you'd have a new kind of symbol table file that held a
'lightly' compiled package spec for limited withs. And you'd use the time
stamp of that file to create the serial number for obsoleteness checking.
Clearly, when you 'really' compiled the spec, the new kind of package spec
would have to be updated (otherwise, it wouldn't necessarily match the
source file, because the source file could have been edited). But that would
necessarily change the serial number, and then the links would fail. And
there would be no way to avoid the problem.

Given that there is no reliable way to determine if a file has been changed
(file time stamps are too crude, often changing when nothing actually is
changed, and aren't kept to enough accuracy on Windows), I don't see any way
to implement this. The compiled=changed model works fine for Ada 95, but is
a complete failure for limited with. Essentially, you have to adopt a
source-based model, and that is a major change.

This also has a giantic impact on build tools. They'd have to be able to
process this new clause and come up with an appropriate order in the face of
cycles. Of course, tools just punt if that happens now.

It may be I just haven't thought about it enough. But I have a lot of things
that I need to do before I leave for the meeting, and thinking about this
isn't high on the list.

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

From: Tucker Taft
Sent: Monday, February  3, 2003  8:53 PM

Randy's comments make me notice something
a bit unfortunate about the limited-with
proposal.  Although we don't need a new
kind of compilation unit, we do need a new
way of compiling a unit in the non-source-based
environment.  (I'll call this new way of compiling
"preregistering" the unit.)  Supporting limited-with
means that either the programmer preregisters
all compilation units on the off chance they might
be mentioned in a limited-with, or the compilation-order-
determination tool gets smarter and figures out which
units need to be "preregistered" and which don't.

Having this additional step could have significant
ripple effects into "make" scripts, regression
testing scripts, ACATS testing scripts, compilation-order-
determination tools, etc.  It also means a new
step to explain to programmers.

None of the above is particularly rocket science, but
it does add to the implementation and "deployment" cost
of this proposal.

In general, I think this might emerge as a very
elegant solution, possibly as elegant as the package
prefix/abstract approach, but significantly more
implementation effort than either the "type C.T;"
or the possibly modified/restricted type stub proposal.

By the way, although it sounds like implementing
"type C.T;" in Randy's compiler might be a bit
more work than a restricted type stub, it seems like that is
small potatoes compared to the heavy lifting involved
in the limited-with proposal.

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

From: Robert Dewar
Sent: Monday, February  3, 2003  8:53 PM

BY the way, this discussion of what is and is not required of compilers
reminds me that I think it would be good to have an annex for the standard
that defined a standard interchange format, right down to the bit-level
coding and file format. That would for example, partly satisy Dan's
concern about incomaptible source representation issues.

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

From: Gary Dismukes
Sent: Tuesday, February  4, 2003  1:28 AM

> But it seems like whatever solution to the mutual recursion problem we
> choose, we really need to solve the problem of proliferation of silly
> type conversions.  Otherwise, these proposals will be nearly unusable.

I tend to agree.  Certainly with the latest "limited with" proposal there
are generally going to be multiple access types for the same designated type
and that's true for most of the other proposals as well.  It seems though
that the models using child packages may be less prone to that since
the child package could use an access type declared in the parent
and reexport using a subtype if needed.

> That is, there ought to be an *implicit* conversion between access types
> in the "safe" cases.  I know we discussed that before.  Is it part of
> any of these proposals, or is it a separate AI?  I seem to recall that
> it might be wrapped up with other access-type issues, like allowing "not
> null" constraints, and access-to-constant parameters.

Perhaps you're thinking of AIs 230 (generalized access types) and 231
(access-to-constant parameters and null-excluding subtypes).  AI-230
would allow more things to be declared using anonymous general access
types, which would increase the number of contexts where implicit
conversions can occur, though I'm not sure how much help that will be
for the mutual-dependence cases where there will be multiple distinct
access types and pointers of those distinct types floating around.
I suppose it will help to some degree.  In any case, I think it's
probably best not to wrap a requirement for easier conversions in with
the AI-217 proposals, as I think that may tend to muddy the discussion
(even more more than it already is;).

> > And I do still like that "type C.T;" ;-).
> > (what a pain I can be...)
>
> That's a fine solution, IMHO.  Your earlier e-mail about
> mutually-recursive types being part of the same abstraction
> makes me comfortable with the idea of tying the solution to
> child packages.  (See, it *is* possible to convey sensible information
> about purely aesthetic concerns.  ;-))
>
> But I would choose the "limited with" idea, unless it is considered too
> hard to implement.

My feeling as well.  Tucker's modified child package proposal definitely
seems the simplest both in description and implementation effort
(well, I guess Randy doesn't agree with that...), but like others
I'm somewhat dissatisfied with requiring a child-based model to
get mutual recursion.  I was finding my mind being drawn back to
wishing we could just do things as simply as other languages are
able to, but had decided that such an approach would be tilted too
much in favor of source-based compilers.  But now that Pascal's
bravely put forward that very suggestion, I'm happy to line up behind
it if it stands up to scrutiny.  I think the critical question is how
much work that approach will be for non-source-based implementations.

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  4:04 AM

> Perhaps you're thinking of AIs 230 (generalized access types) and 231
> (access-to-constant parameters and null-excluding subtypes).  AI-230
> would allow more things to be declared using anonymous general access
> types, which would increase the number of contexts where implicit
> conversions can occur, though I'm not sure how much help that will be
> for the mutual-dependence cases where there will be multiple distinct
> access types and pointers of those distinct types floating around.
> I suppose it will help to some degree.

AI 230 looked quite promising, but it has been on Tuck's action item list for
about two years, and he didn't do his homework (but then he'll have some time on
the plane, so you never know...)

> In any case, I think it's
> probably best not to wrap a requirement for easier conversions in with
> the AI-217 proposals, as I think that may tend to muddy the discussion
> (even more more than it already is;).

Agreed.

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

From: Robert A Duff
Sent: Tuesday, February  4, 2003  4:11 PM

I don't want to muddy the discussion *too* much.  But many (if not all)
of the cyclic-dependency solutions are going to introduce extra access
type decls.  I don't want folks to reject proposals for that reason.
Therefore, we need to at least keep an open mind -- assume that the
access-type-conversion annoyance can be solved.

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  3:02 AM

> In Pascal's proposal, a parent package P can weakly 'with' its children:
> "weak with P.C;", if you want a mutually dependent pair of types,
> one in P and one in P.C.  The spec of P will be elaborated before the
> spec of P.C, as usual.
>
> However, it is pointless for P.C to say "weak with P;", because the
> child-to-parent dependency is necessarily strong (given that there's no
> proposal to add syntax saying otherwise -- "weak children?").
>
> No problems here.  I'm just making some observations.

In my proposal it would also be possible for unit P to say "limited with P", but
I think we would want to forbid that, as it seems methodologically dubious.  (If
you follow the model to its logical conclusions, it would for instance allow
forward references to packages and types within the spec of P.)

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  3:21 AM

>  The first is where you have a type declaration that depends on another
> type:
>
> type Color is (Red, Green, Blue);
> type Properties(C: Color) is...;
>
> Does the incomplete type for Properties include the discriminant?

I have no doubt that the incomplete type for Properties doesn't include the
discriminant.  We discussed that issue when we did "with type" and we concluded
that any other choice was causing endless trouble.

Discriminants of incomplete types are not too useful anyway, they can only be
used in building contrained access-to-object types.  And the added complexity
is not worth it.

> I prefer the choice where only private, record and tagged
> types are incomplete in the limited view.

But then you would need to do a lot of semantic analysis, and you would have to
define very precisely what happens during a "superficial" compilation.
Consider for instance:

    type R is record ... end record;
    type T is new Integer range R'Alignment .. R'Size;

How can T be properly defined if R is incomplete?  And don't tell me that T
would be some new kind of "incomplete integer type", because that would add so
much complexity to the language that I'd rather forget about the entire
proposal.

As Tuck pointed out in his "willies" message, the only way that this model can
be made to work is if the first phase can be performed by a brain-dead parser.

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

From: Robert I. Eachus
Sent: Tuesday, February  4, 2003  11:38 AM

Pascal Leroy wrote:

>I have no doubt that the incomplete type for Properties doesn't include the
>discriminant.  We discussed that issue when we did "with type" and we concluded
>that any other choice was causing endless trouble.

About 4 AM, I realized that what is needed is the fact that Properties
is a type with discriminants, notthing more.  But we already have that
concept well defined, and it covers incomplete tagged types. Then I went
back to sleep.  So I propose that (incomplete) tagged types and types
like Properties have unknown discriminants.  This allows incomplete
discrete types to be treated like other incomplete types, and incomplete
array types to also be treated as incomplete with unknown discriminants.

>But then you would need to do a lot of semantic analysis, and you would have to
>define very precisely what happens during a "superficial" compilation.  Consider
>for instance:
>
>    type R is record ... end record;
>    type T is new Integer range R'Alignment .. R'Size;
>
>How can T be properly defined if R is incomplete?  And don't tell me that T
>would be some new kind of "incomplete integer type", because that would add so
>much complexity to the language that I'd rather forget about the entire
>proposal.

The nightmare that woke me up was not quite this case, but related.
 (Using T'First, etc., of another discrete type.Hiding the literals is
not enough.)

As for access types, I still think we need to special case those.  At
first I was only worried about the proliferation of access types with
the same target.  But then I realized that the type matching nightmare
gets worse in cases where some of the potential access types are hidden
by incompleteness.  The messy case is X: T := F.all.  F can be an
overloaded function, and I would hate to have the legality checked
twice, especially if one of the checks depended on elaboration order.

There is another painful case though, and I am not sure what to do about
it...

limited with B;
package A is
   type Foo is record...end record;
   ...
end A;

with A;
package B is
    type TA is access A.Foo;
    type Bar is record
       TAC: TA;
    end record;
    ...
end B;

(You can produce variations on this theme by creating a third package in
the cycle.) We are not talking pathological here, this is part of the
core functionality.  Now, what is the status of B.TA inside the spec of
A?  If it is incomplete we get access type proliferation.  If it is an
access type, is it an incomplete access type? Inquiring minds want to know.

If you prefer, write the example as:

limited with A.B;
package A is
    type TA is access B.T;
    ...
end A;

package A.B is
   type T is tagged record...end record;
   ...
end A.B;

I can live with this version working where the first one breaks, but now
we get back to the issue of representation of the access type.  Whether
it has to be a fat pointer or not will depend on features of T.  Again,
I can live with that, we just have to determine which features are
visible.  (Unknown discriminants for T in this case seems minimal.)

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

From: Tucker Taft
Sent: Tuesday, February  4, 2003  2:20 PM

"Robert I. Eachus" wrote:

> About 4 AM, I realized that what is needed is the fact that Properties
> is a type with discriminants, notthing more.

Why?

>... But we already have that
>concept well defined, and it covers incomplete tagged types. Then I went
>back to sleep.  So I propose that (incomplete) tagged types and types
>like Properties have unknown discriminants.  This allows incomplete
>discrete types to be treated like other incomplete types, and incomplete
>array types to also be treated as incomplete with unknown discriminants.

Why can't we treat these all as plain old incomplete types?
You haven't explained that.


>>But then you would need to do a lot of semantic analysis, and you would have to
>>define very precisely what happens during a "superficial" compilation.  Consider
>>for instance:
>>
>>    type R is record ... end record;
>>    type T is new Integer range R'Alignment .. R'Size;
>>
>>How can T be properly defined if R is incomplete?  And don't tell me that T
>>would be some new kind of "incomplete integer type", because that would add so
>>much complexity to the language that I'd rather forget about the entire
>>proposal.
>>
>The nightmare that woke me up was not quite this case, but related.
> (Using T'First, etc., of another discrete type.Hiding the literals is
>not enough.)

I am totally lost.  These types are incomplete.  You can't do anything
with them.

> As for access types, I still think we need to special case those.  At
> first I was only worried about the proliferation of access types with
> the same target.  But then I realized that the type matching nightmare
> gets worse in cases where some of the potential access types are hidden
> by incompleteness.  The messy case is X: T := F.all.  F can be an
> overloaded function, and I would hate to have the legality checked
> twice, especially if one of the checks depended on elaboration order.

I must be missing something fundamental here.  All of these types are
just plain old incomplete types.  Yes, I agree we ought to special
case tagged and allow a bit more, but that seems irrelevant to
what you are talking about.  What is messy about the messy case?

>...
>(You can produce variations on this theme by creating a third package in
>the cycle.) We are not talking pathological here, this is part of the
>core functionality.  Now, what is the status of B.TA inside the spec of
>A?  If it is incomplete we get access type proliferation.  If it is an
>access type, is it an incomplete access type? Inquiring minds want to know.

It is incomplete and we get access type proliferation.

I don't see that "limited with" can solve the access type proliferation
problem, and trying to make it do so is doomed, in my mind.

...
>I can live with this version working where the first one breaks, but now
>we get back to the issue of representation of the access type.  Whether
>it has to be a fat pointer or not will depend on features of T.  Again,
>I can live with that, we just have to determine which features are
>visible.  (Unknown discriminants for T in this case seems minimal.)

I am presuming that all non-tagged types are incomplete, period.  No new
notion of "incomplete access types" is being added.

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  3:00 PM

> > So I propose that (incomplete) tagged types and types
> > like Properties have unknown discriminants.  This allows incomplete
> > discrete types to be treated like other incomplete types, and incomplete
> > array types to also be treated as incomplete with unknown discriminants.
>
> Why can't we treat these all as plain old incomplete types?
> You haven't explained that.

I am as puzzled as Tuck.  It is my understanding that unknown discriminants
for incomplete types are pretty much equivalent to no discriminants.  So
it's fine with me to insist that these types have unknown discriminants, but
it doesn't make the slightest difference.  (Remember, we are talking
_incomplete_ types here, so you cannot do much with them anyway.)

> I am presuming that all non-tagged types are incomplete, period.  No new
> notion of "incomplete access types" is being added.

Agreed.  This seems like the only way to avoid madness.  Granted, this
proposal doesn't solve the problem of proliferation of access types, but
that problem shows up in contexts that are unrelated to circular type
dependencies, so I think that it should be addressed by a different
mechanism.

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

From: Randy Brukardt
Sent: Tuesday, February  4, 2003  2:53 PM

Tucker said, replying to Robert Eachus:
> > If it is incomplete we get access type proliferation.  If it is an
> > access type, is it an incomplete access type? Inquiring
> > minds want to know.
>
> It is incomplete and we get access type proliferation.
>
> I don't see that "limited with" can solve the access type proliferation
> problem, and trying to make it do so is doomed, in my mind.

At which point, I lose interest in the proposal.

All of type stubs, restricted type stubs, and Tuck's type C.T idea do solve
the access type proliferation problem. Admittedly the structure of the
declarations is unnatural. But the only real alternative is to adopt
something like AI-230. That bulks up the proposal a ton.

So, limited with:
   -- Is much harder to implement in library based compilers. It's probably
harder to implement than type stubs/type C.T in source-based compilers as
well (certainly no easier). Affects compilation tools in all environments;
   -- Doesn't solve the access type proliferation problem.

The only advantage seems to be:
   -- A more natural expression of package structure.

But that's dubious, since you need to resort to unnatural package structures
in order to share access types. So you're pretty much left with "much harder
to implement".

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  3:09 PM

> > I don't see that "limited with" can solve the access type proliferation
> > problem, and trying to make it do so is doomed, in my mind.
>
> At which point, I lose interest in the proposal.

Access type proliferation in and of itself is not a problem.  What is a
problem is access type conversions.  That problem shows up in all sorts of
contexts, notably in OOP where you have access types (class-wide or
specific) designating some type in a hierarchy, and all conversions must be
explicit, even those that cannot possibly fail.  That is a big pain.

This has nothing to do with circular type dependences.  Even if we select a
solution to the problem of circular type dependences that doesn't cause
proliferation of access types, the problem of access type conversions will
remain.

In C, if you have a type t you automatically get a pointer type t*.  In Ada,
I often wish we had a similar capability, where I wouldn't have to declare
all these silly access types and convert between them explicitly.

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

From: Robert I. Eachus
Sent: Tuesday, February  4, 2003  5:20 PM

Tucker Taft wrote:

>It is incomplete and we get access type proliferation.
>
>I don't see that "limited with" can solve the access type proliferation
>problem, and trying to make it do so is doomed, in my mind.

And in my mind the proposal is doomed (or not the best one on the table)
if it doesn't address the access type proliferation problem.

>I am presuming that all non-tagged types are incomplete, period.  No new
>notion of "incomplete access types" is being added.

Hmm.  I must not be making myself clear.  My idea is not to add
"incomplete access types," but to try to make the limited with idea work
with access types to these new special incomplete types.  The refractory
issue is when you have access types that carry descriptor information
and when you don't.  Where that data is stored is irrelevant.  Even if
you store the descriptor for an unconstrained array type with the data
and pointers to both unconstrained and constrained arrays are the same
size, some conversions and assignments are impossible or require
recopying the data:

package P is
     type Str is new String;
     subtype Str4 is Str(1..4);
     type PT1 is access Str;
     type PT2 is access Str4;
end P;

or if you prefer:

limited with P;
package Q is
  type PT3 is access P.Str;
  type PT4 is access P.Str4;
end Q;

Now I need to compile some code--perhaps the body of Q--that needs to do
those damned explicit type conversions, or even just some simple
assignments.  As a programmer I have a problem, but without more
information about the types in P than Tuck seems willing to allow, the
compiler is SOL.  It doesn't know how to create objects of type PT3
and/or PT4, and even has trouble with legality issues:
...
PO3: PT3;
PO4: PT4;
...
PO3.all := PO4.all; -- legal?

Right now the voting seems to be that either the limited with idea is
doomed because it is too much work, or it is doomed because it can't
deal with the issue of access type proliferation.  I'll let sleeping
dogs lie.

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

From: Tucker Taft
Sent: Tuesday, February  4, 2003  5:58 PM

"Robert I. Eachus" wrote:
>
> Tucker Taft wrote:
>
> >It is incomplete and we get access type proliferation.
> >
> >I don't see that "limited with" can solve the access type proliferation
> >problem, and trying to make it do so is doomed, in my mind.
> >
> And in my mind the proposal is doomed (or not the best one on the table)
> if it doesn't address the access type proliferation problem.

Well, I think trying to make it address this problem very tricky,
since it means looking at the designated subtype indication,
and that will get you back to worrying about use visibility, etc.

> >I am presuming that all non-tagged types are incomplete, period.  No new
> >notion of "incomplete access types" is being added.
> >
> Hmm.  I must not be making myself clear.  My idea is not to add
> "incomplete access types," but to try to make the limited with idea work
> with access types to these new special incomplete types.  The refractory
> issue is when you have access types that carry descriptor information
> and when you don't.  Where that data is stored is irrelevant.  Even if
> you store the descriptor for an unconstrained array type with the data
> and pointers to both unconstrained and constrained arrays are the same
> size, some conversions and assignments are impossible or require
> recopying the data:

I am getting an inkling of your concern.  At least one of the
problems you seem to be worrying about is an implementation
issue (as opposed to a semantics issue), and is one that bedevils
the "unrestricted" type stub proposal, and the "with type"
proposal.  Namely that when choosing a representation for access types
to the full type, the compiler may not be aware that there are
access types to the incomplete type.  I don't think this is quite
as serious as the problem with the "with type" proposal, since
the compiler is doing some serious peeking at the full type
when it creates the incomplete type, so *if* it has multiple
access type representations, it can mark the incomplete type
with enough extra information so that it will choose an appropriate
representation for any access type that is declared later which
"sees" only the incomplete type.

Unfortunately, derived types create a bit of a problem, since they
don't reveal syntactically what sort of type they are (e.g.,
whether their first subtype is an unconstrained array subtype and
hence pointers to it should be default be "fat" pointers).
This would mean that the "peeker" would have to make a "guess"
about whether the incomplete type is an unconstrained array.
Probably it would assume all derived types are not
unconstrained arrays.  In any case, whatever guess it makes,
it would want to record that in the limited view of the package.
When the full view is compiled, as part of the "compatibility"
check it would also have to check that the "guess" was correct.
If the guess was wrong, and the type is in fact an unconstrained array,
say, then it would have to record that fact on the incomplete
type, and bump the timestamp (or equivalent) of the limited view
of the package.  Ultimately the units that depend on this limited
view would get recompiled.

For a source-based compiler, there isn't really any place to
permanently record the "correct" answer, so it would probably
end up being a link-time check, and if there were an incompatibility,
the offending units would be recompiled with a switch directing
them to treat the problematic incomplete type as an unconstrained array,
even though it wasn't obvious.


>
> package P is
>      type Str is new String;
>      subtype Str4 is Str(1..4);
>      type PT1 is access Str;
>      type PT2 is access Str4;
> end P;
>
> or if you prefer:
>
> limited with P;
> package Q is
>   type PT3 is access P.Str;
>   type PT4 is access P.Str4;

This wouldn't be legal, since Str4 is defined by a subtype, not
a type declaration.

> end Q;
>
> Now I need to compile some code--perhaps the body of Q--that needs to do
> those damned explicit type conversions, or even just some simple
> assignments.  As a programmer I have a problem, but without more
> information about the types in P than Tuck seems willing to allow, the
> compiler is SOL.  It doesn't know how to create objects of type PT3
> and/or PT4, and even has trouble with legality issues:

I don't see how legality issues enter into the problem of access
type representation.  You can't do much of anything with incomplete types.
And you can't dereference access-to-incomplete, unless perhaps
there is a complete type "nearby".

> ...
> PO3: PT3;
> PO4: PT4;
> ...
> PO3.all := PO4.all; -- legal?
>
> Right now the voting seems to be that either the limited with idea is
> doomed because it is too much work, or it is doomed because it can't
> deal with the issue of access type proliferation.  I'll let sleeping
> dogs lie.

I don't think it has been abandoned, so it is still important
to try to identify (real ;-) problems associated with the
proposal.

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

From: Robert I. Eachus
Sent: Tuesday, February  4, 2003  6:34 PM

Tucker Taft wrote:

>Well, I think trying to make it address this problem very tricky,
>since it means looking at the designated subtype indication,
>and that will get you back to worrying about use visibility, etc.

Yep.

...
>For a source-based compiler, there isn't really any place to
>permanently record the "correct" answer, so it would probably
>end up being a link-time check, and if there were an incompatibility,
>the offending units would be recompiled with a switch directing
>them to treat the problematic incomplete type as an unconstrained array,
>even though it wasn't obvious.

Yep, it is a problem in any solution to the mutual dependence problem.
 The issue here is deciding what aggrevations to add--to the user, the
implementor, or both.  We could make fairly draconian rules that stated
that any unit that actually uses a subtype, access type to, or type
derived from one of these magic incomplete types  must depend on the
limited withed unit, and thus see the full type.  I think so far this
has been an implicit assumption, but the devil is in the details.

To take your discussion above a bit further, what happens when you
modify the limited withed unit in a way that changes the necessary
representation of an access type declared elsewhere?  It begins to look
like a very real dependence somehow magically wished away.  You end up
having to recompile some units due to changes in units they don't depend
on....

>I don't think it has been abandoned, so it is still important
>to try to identify (real ;-) problems associated with the
>proposal.
>
Consider the (dead) horse to have been additionally flogged.

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

From: Robert A Duff
Sent: Tuesday, February  4, 2003  7:06 PM

Note that we already have a case where you can create
access-to-incomplete without knowing what the complete type
looks like.  Namely, an incomplete type declared in a package
spec, and completed in the body.

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

From: Robert I. Eachus
Sent: Tuesday, February  4, 2003  10:34 PM

What did we used to call that?  Ah, yes, I remember the Tucker Taft
amendment. ;-)  The only good thing about it was that the incomplete
type had to be private, so it couldn't be seen outside the package.

If you weren't following LMC/ARG actions then look up (Ada-83) AI-7.
 For one meeting, John Goodenough put the AI's for consideration in two
books--AI-2 and AI-7, plus related AI's, and everything else.  (The
final version of AI-00007/19-BI-WJ--yes that is version 19--is actually
a consolidation of about a dozen separate AI's)

As I recall, AI-7 underwent combinatorial explosion, we discovered lots
of nits and crannies, and finally came up with a smoking gun case that
did not require the TTA to fire off.  But if you think my concern about
access to incomplete types with discriminants  is overdone, you really
should go back and look at all those AI's.  As I recall, the final
version of AI-7 allowed the legality check on discriminant constraints
of access to incomplete objects to be done in one of three
places--because there were cases when each of the three wouldn't work,
and in some cases more than one.

Any "solution" to the interlocking types problem that re-opens that can
of worms should be shot then burned and the ashes stirred and buried
under a crossroads at midnight.  I think net that AI-7 and its relatives
took the equivalent of several three-day meetings to resolve. (This was
"fixed" in Ada 95 by deferring all compatibility checks until object
creation.

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  3:55 AM

> At the moment, I think it's too hard to implement (given that type stubs are
> expected to take 4 hours of work; this would be more like 4 weeks). But that
> may be irrelevant. More important is that I don't see any way, within the
> current design of Janus/Ada, to implement something like this.

Not sure how you came up with the 4 hours estimate, but I'm sure it would take
me more than that to do a detailed design.  At this point I'd say that the
implementation effort for any of these proposals would be in the range 4 weeks
.. 4 months, and I can't be more specific without doing a detailed analysis.
But anyway, I don't think that numbers are very interesting at this point.  It's
really the essence of the implementation difficulties that need to be looked at.

As far as I can tell, you and I have the same set of problems, because we are
both library-based.  I realize that the devil is in the details, and that the
magnitude of changes can vary substantially from one implementation to another,
but I don't buy that the "limited with" proposal would force you to go to a
source-based model.  (For Rational, going to a source-based model is a no-no;
we'd rather get out of the Ada business; so hopefully there has to be an
implementation technique where we stick to our current library model.)

> The problem is with the obsoleteness check. Janus/Ada uses a serial number
> created from the time stamp of the symbol table file created for a library
> unit for checking. Also, the Janus/Ada compiler knows nothing about source
> files other than the one it is compiling at the moment. (Other tools are a
> bit smarter, but the compiler does not use that information itself.) It only
> knows about symbol table files.

Fine.  If I substitute "Diana tree" for "symbol table", that's essentially true
of Apex too.  (Ignoring all the incremental compilation crap.)

> One presumes that you'd have a new kind of symbol table file that held a
> 'lightly' compiled package spec for limited withs. And you'd use the time
> stamp of that file to create the serial number for obsoleteness checking.
> Clearly, when you 'really' compiled the spec, the new kind of package spec
> would have to be updated (otherwise, it wouldn't necessarily match the
> source file, because the source file could have been edited).

And we would indeed have the same problem.  We would first created a "parsed"
Diana tree (during "superficial" compilation) and then we would rewrite it to
make it "analyzed" (during "full" compilation).  Surely that would change the
timestamp.

> But that would
> necessarily change the serial number, and then the links would fail. And
> there would be no way to avoid the problem.
>
> Given that there is no reliable way to determine if a file has been changed

The way that I plan to do this is to store in the "parsed" Diana tree (what you
call the "light symbol table") a hash code.  This hash code could be based on
the sequence of tokens in the source, or it could be more clever and be based
only on the names of types and packages (the information that is available after
"superficial" compilation).  Whenever clients currently use a timestamp to check
for obsolescence, they would use the combination <timestamp, hash>.  If the
timestamps match, everything is fine.  Otherwise, the Diana trees need to be
opened, and the hashes need to be compared.  I realize that this will result in
some extra open system calls, but Ada's separate compilation already requires a
zillion system calls, so I won't lose any sleep on that.

Of course, a hash code is not exactly 100% safe, as there could be collisions.
But if you design it well enough, you won't see a collision in your lifetime.
(We use 96-bit hash codes all over the place for incremental compilation, and we
have never run into a collision in all these years.)

> This also has a giantic impact on build tools. They'd have to be able to
> process this new clause and come up with an appropriate order in the face of
> cycles. Of course, tools just punt if that happens now.

But the trick is that there are no prerequisites for "superficial" compilation.
So one approach is to blindly run "superficial" compilation over all the units,
in any order you like (e.g. the order of i-nodes in the filesystem if you like).
And then run "full" compilation in an order compatible with normal with clauses
and other semantic dependencies, just like you do now (and ignoring "limited
with" clauses).  No need to process the new clause, no need to change the
algorithm that determines the compilation order.

Of course you might want to be more clever and avoid the "superficial" phase if
it's not needed, but that's an optimization, not something you would have to do
for correctness.

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  4:00 AM

> Supporting limited-with
> means that either the programmer preregisters
> all compilation units on the off chance they might
> be mentioned in a limited-with, or the compilation-order-
> determination tool gets smarter and figures out which
> units need to be "preregistered" and which don't.

As I mentioned in my previous response to Randy, it would be just fine if the
compilation-order-determination tool were to systematically preregister each and
every unit, and then run the normal ordering algorithm, ignoring "limited with"
clauses.  I would think that compiler writers would prefer to go that way,
rather than breaking each and every compilation script in the universe.  So I am
arguing that the new step should be essentially invisible to programmers.

In an implementation where the preregistration step would be costly, then yes,
the ordering algorithm might have to be modified to avoid preregistration when
possible.

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

From: Erhard Ploedereder
Sent: Tuesday, February  4, 2003  7:38 AM

I like "limited with". It is the closest we got to what the user expects.
I would like to see a writeup.

(I have had bellyaches with Tuck's proposal of tying the cyclic dependency
 to child packages. I waited for the aches to go away. They did not.)

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

From: Randy Brukardt
Sent: Tuesday, February  4, 2003  3:00 PM

> > At the moment, I think it's too hard to implement (given that type stubs are
> > expected to take 4 hours of work; this would be more like 4 weeks). But that
> > may be irrelevant. More important is that I don't see any way, within the
> > current design of Janus/Ada, to implement something like this.
>
> Not sure how you came up with the 4 hours estimate, but I'm sure it would take
> me more than that to do a detailed design.

I did a detailed design (in my head) when I was unable to sleep Saturday
night.

> At this point I'd say that the implementation effort for any of these proposals
> would be in the range 4 weeks .. 4 months, and I can't be more specific without
> doing a detailed analysis. But anyway, I don't think that numbers are very
> interesting at this point.  It's really the essence of the implementation
> difficulties that need to be looked at.

The reason I was thinking about it was that the restricted type stubs model
pretty much matches how Janus/Ada works internally anyway. So the main cost
is "connecting" the stub to the completion. (And many stubs works, because
it's a one-way pointer. But I wouldn't expect that to be true on other
implementations.) Since incomplete and private types use the same code in
most circumstances, the visibility implications of "availability" should be
free. (That is the biggest assumption, of course, because there are no such
rules in Ada 95.) So all of the work is doing the connection, and a brute
force version (walk all of the symbol table looking for stubs, then when a
stub is found, walk the whole symbol table looking for a connection) would
only need to be called in one place. Most of the work is figuring out where
to call that routine. A better version would save the stubs in a list as
they are loaded, so we wouldn't have to search for them. That version would
have no distributed cost and not a lot of cost even when stubs are used, so
I doubt its possible to do better.

Tucker's C.T adds a lot of messing around with ghost packages, but otherwise
is identical (we don't have to look quite so far for a completion, but the
process is the same). Which is why I'm certain its more work.

The current stub stubs requires two stubs of the same completion to match,
even when the completion isn't available. That's more work, but I don't
think it's a very significant amount (it's just another weird special case
in the type matcher, just like anonymous access types and T'Class).

In all of these cases, I wouldn't be surprised to find glitches, but that
would require a pretty complete test suite. Building that would take longer
than doing the implementation (that is often true).

Limited with would require a new kind of symbol file, or something (Pascal
suggests a hash). It also would require going in an adding a bunch of stuff
to the make tool. No one understands how that thing really works, so that
alone is a daunting project. And the failure to provide any sort of solution
to the access type problem makes it feel like "with type" all over again
(which isn't surprising, because it *IS* essentially "with type", just as
"type C.T" is just a different syntax for type stubs).

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  3:17 PM

> Since incomplete and private types use the same code in
> most circumstances, the visibility implications of "availability" should be
> free.

Aren't you concerned about types having three views (incomplete, partial,
full) in the case of an incomplete type completed by a private type?  That
gives me the willies, to borrow Tuck's words, because it's currently very
complicated in our compiler to decide what properties of a type are visible
at a given place (because of the separation between partial and full view,
because of characteristics that become visible "later within the immediate
scope", etc.).  Adding a third view is not going to make this easier.

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

From: Randy Brukardt
Sent: Tuesday, February  4, 2003  3:43 PM

No, because we have separate type records for each view, and they have their
own visibility. For type matching purposes, the only question is whether you
can or cannot walk the pointer to the 'next' view, as we always match (and
use for other purposes) the "fullest" view available. That depends on
visibility, etc. (We have a separate set of routines that always goes to the
completion, which the intermediate code generation uses. To heck with
visibility at that point.) Since this stuff is recursive (well, actually it
uses a loop), you'd first try to go from the incomplete block to the private
block (depending on the visibility of the private), and then, once you had
the private, you'd do the checks that we currently have. So I don't see a
problem with three parts or ten parts for that matter.

But I realize that if you somehow managed to implement types with a single
record for both views (we tried that and concluded it was impossible. But
perhaps we weren't clever enough. Or perhaps its impossible because of
shared generics.), it might be much worse.

But I think that any proposal has to allow stubs (or whatever) of private
types. So you're going to have the three view issues, and if that is a
problem, any solution to this problem will be a nightmare to implement. In
which case you ought to stay out of the implementation difficulty debate
altogether. :-) :-)

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

From: Gary Dismukes
Sent: Tuesday, February  4, 2003  3:20 PM

Pascal wrote:
> In my proposal it would also be possible for unit P to say "limited with P", but
> I think we would want to forbid that, as it seems methodologically dubious.  (If
> you follow the model to its logical conclusions, it would for instance allow
> forward references to packages and types within the spec of P.)

One question is, what does it mean when you say "limited with P.C.D".
Is it equivalent to having limited with for P, P.C, and P.C.D, in
analogy with normal with clauses?  That was my first thought, and
it seems natural to make it behave the same, but perhaps it's more
reasonable to say it only applies to the final named unit.  That
would avoid the issue of self-circular references when a parent
withs its child and we would also want the rules to disallow
a direct limited with of yourself.

Another small issue is whether use clauses are allowed for packages
named in these with clauses.  I imagine that the implementation is
effectively going to create a package entity containing an incomplete
type representative for each type in the real package's visible part
(along with representatives for any nested packages presumably)
and since this will look essentially the same as other packages
in the symbol table environment I don't see any technical problem
with permitting a use clause for these packages.  Perhaps there are
methodological reasons for not allowing it, though I can't think
of any.  Changing a limited with to a normal with could introduce
new illegalities due to name clashes, but that's no worse than
adding a new with and use clause for some package.

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

From: Pascal Leroy
Sent: Tuesday, February  4, 2003  3:30 PM

> One question is, what does it mean when you say "limited with P.C.D".
> Is it equivalent to having limited with for P, P.C, and P.C.D, in
> analogy with normal with clauses?  That was my first thought, and
> it seems natural to make it behave the same, but perhaps it's more
> reasonable to say it only applies to the final named unit.  That
> would avoid the issue of self-circular references when a parent
> withs its child and we would also want the rules to disallow
> a direct limited with of yourself.

From a pedagogical standpoint it would seem simpler to say that "limited
with" works like "with", i.e. that "limited with P.C.D" is equivalent to
"with P, P.C, P.C.D".  Any other option is going to unnecessary confuse
users.

So maybe the right answer is that if a package has a "limited with" of
itself it has no effect (but is not illegal).

> Another small issue is whether use clauses are allowed for packages
> named in these with clauses.  I imagine that the implementation is
> effectively going to create a package entity containing an incomplete
> type representative for each type in the real package's visible part
> (along with representatives for any nested packages presumably)
> and since this will look essentially the same as other packages
> in the symbol table environment I don't see any technical problem
> with permitting a use clause for these packages.

Yes, I was thinking that use clauses and package renamings would be OK in
that context, although if they lead to problems we could disallow them.  But
I can't think of any problem at this point.

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

From: Robert Dewar
Sent: Tuesday, February  4, 2003  10:19 PM

> In C, if you have a type t you automatically get a pointer type t*.  In Ada,
> I often wish we had a similar capability, where I wouldn't have to declare
> all these silly access types and convert between them explicitly.

For some reason, I never really understood it, this proposal, which I was strongly
in favor of, never got significant support (I had suggested early on calling it
type'access).

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

From: Tucker Taft
Sent: Tuesday, February  4, 2003  3:27 PM

In discussing this further with Bob Duff, and mulling it over
more in my mind, I see the following restrictions would probably
be needed on the "limited with" capability.  I am using the term
"limited view" of a package to be what you get when you mention
a package in a limited-with-clause.  I am presuming that a limited
view of a package contains only nested packages and types, and all
the types are [tagged] incomple.

A "limited with" *cannot* make the following declarations visible:
  1) Package instantiation
       Because the generic being instantiated needs to be visible
       if we are to determine what types it contains, and identifying
       the generic may require resolving a name that is only use-visible,
       or that is from some other compilation unit

  2) Package renaming
       Because the package being renamed needs to be visible
       if we are to determine what types it contains, and identifying
       the renamed package may require resolving a name that is only use-visible,
       or that is from some other compilation unit

A "limited view" of a package *cannot* be mentioned in:
  3) A "use" clause
       Because we can get a funky kind of Beaujolais effect if we have
       a "use" clause for two different packages, and due to
       changing a "normal" with clauses inherited from some ancestor unit
       the meaning of an identifier switches from one thing to another; e.g.:

        package A is
             X : Integer := 7;
        end A;

        package B is
             X : Integer := 203;
        end B;

        with A;  -- change this to "with B;" and see what happens
        package P is end;

        limited with A, B;
        use A;
        use B;  -- Probably shouldn't be allowed if only have limited view
        package P.C is
            Y : Integer := X;  -- which X is this?
        end P.C;

       This is presuming that if we were to allow a "use" clause for a limited
       view of a package, it would make only the types and subpackages directly visible.
       If the "use" clause made everything in the package visible part
       visible, but only the types were actually "usable" that could work,
       but having visible but unusable declarations of *all* kinds,
       rather than just types and packages, could significantly increase the effort
       of building up a limited view.

  4) A package renaming declaration
      This restriction is probably not as critical as the others.
      The problem comes when someone from outside the unit containing
      the limited with references this package renaming.  What does it see?
      If it has a "regular" with for the target of the limited-with, does
      it see the "full" view of the package via the renaming, or only the
      limited view of it.

Possible restriction (4) brings up the issue of the opposite situation, where
a given unit has a limited view on a package, but also has visibility
on a renaming of a full view of the package.  There seem to be various
possibilities:

   a) The renaming also provides only a limited view when referenced from the given unit.
   b) The given unit has a full view of the package, through either the
      renaming or the name introduced by the limited-with clause
   c) The given unit has a limited view via the name introduced by the limited-with
      clause, and a full view via the renaming, and they are essentially
      completely unrelated packages.
   d) (c), except the packages are recognized as different views of the same
      package, and the incomplete types in the limited view are recognized
      as being completed in the renaming, and so are treated as non-limited
      types for all intents and purposes.

(a) is probably the most consistent with the way package renamings work now,
in that what children you see via a renaming of a library unit package is determined
by the "with" clauses in the unit referencing the renaming, rather than by the
with-clauses in the unit containing the renaming.

(b) and (d) are both similar to the choice I suggested for "type C.T;" where
if the completing type declaration is visible (including presumably via
a renaming), then the incomplete type declaration is hidden from
all visibility.  In the "type C.T;" proposal, this presumably implies
that it is as though the "type C.T;" declaration were not there at all,
so if a renaming "package R renames C;" is visible, but "C" itself is
not visible (because C was not directly "withed"), then you *cannot*
refer to the type at all via "C.T."   You can refer to it via "R.T"
and then of course it is a full type.

There are other possible ways of handling this for "type C.T;"
but this approach required the smallest change to 18.3(19) and at
least makes some kind of sense.

For all proposals, the issue of visible renamings of the enclosing package
when the package itself is not visible is thorny.  A related question is when the
completing type is *not* visible (including not via a renaming), but a declaration of a subtype
*is* visible, or an object of the type is visible.  The questions are always:

   i) Is the type named via the "limited view" name complete or incomplete?

   ii) If incomplete, does it nonetheless "match" the complete type in certain
       contexts?

To put it more concretely:

  package P1 is
    type A is access <incomplete view of type P2.T>;
      -- incomplete view is visible via a limited with, type stub, type C.T, or whatever
    Y, Z : A;
  end;

  package P2 is
      type T is <full type definition>;
  end P2;

  with P2;
  package P3 is
    X : <subtype of complete view of P2.T>;
  end P3;

  with P1, P3;  -- No "with" of P2, no visibility on P2.T
  procedure P4 is begin
      ...
    P3.X := P1.Y.all;  -- when is this legal?   (*)
    P1.Y.all := P3.X;  -- same question         (**)
    if P1.Y.all = P1.Z.all then  -- same question (***)
      ...

Hopefully the answers to (*) and (**) are the same, though
the asymmetric Name Resolution wording of 5.2(4) on assignment
statements seems like (*) and (**) might be treated differently
unless we are careful.

Note that last time we worked on the "availability" rules for type stubs,
we required that either we be in the scope of a with clause for
the enclosing package (or a renaming thereof, I presume), or we be in a context
where we have the complete type "nearby" (I forget the wording at this
point).  This would say that having a visible (but not "with"ed)
renaming of the enclosing package, or having a visible subtype,
wouldn't help.  For the above cases, this would mean that probably
(*) and (**) would be legal, but (***) would not be legal.

Have a nice day... ;-)

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

From: Tucker Taft
Sent: Tuesday, February  4, 2003  3:48 PM

Pascal Leroy wrote:
> Randy wrote:
> > One presumes that you'd have a new kind of symbol table file that held a
> > 'lightly' compiled package spec for limited withs. And you'd use the time
> > stamp of that file to create the serial number for obsoleteness checking.
> > Clearly, when you 'really' compiled the spec, the new kind of package spec
> > would have to be updated (otherwise, it wouldn't necessarily match the
> > source file, because the source file could have been edited).
>
> And we would indeed have the same problem.  We would first created a "parsed"
> Diana tree (during "superficial" compilation) and then we would rewrite it to
> make it "analyzed" (during "full" compilation).  Surely that would change the
> timestamp...

After talking with Bob Duff about this a bit, I don't think this
really works.  I think you need to keep around indefinitely both a "full view"
of the package and a "limited view" of the package.  Even after you
do a "full" compile, some units can still do "limited with" and should only
see the limited view.  What I would recommend is you treat them as
pretty much distinct units.  When you fully compile, if there is a limited
view already present, you could check that it is compatible with the full view
(i.e. has the same names in the package/type tree, and the same types are
tagged).

I think if they are not compatible, then and only then does it seem worth
obsoleting the limited view and any units compiled against it.  There seems no
point in hashing the incomplete view, since it is easy enough to check
compatibility.  Hashing would only be an optimization if this compatibility
check turned out to be really expensive.

If a unit requests a limited view, and the only non-obsolete thing you have around
is the full view, I would at that point create the limited view from the full view.
I would bump the time stamp on the limited view only when it is created or updated,
and units compiled against the limited view only worry about that time stamp.
The time stamp of the full view is irrelevant to them.

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

From: Randy Brukardt
Sent: Tuesday, February  4, 2003  4:13 PM

"Easy enough to check compatibility"? You've got to be kidding. The only
thing that we know how to do with a symbol file is load them into a symbol
table. But you can't do that to "check compatibility", because you've got to
load the full view into the same place (can't have two packages with the
same library-level name).

Pascal's hash idea (instead of a time stamp) makes much more sense. You'd
create the limited symbol file for every compilation (full and limited) that
you did, but the hash would only change if it actually is different.

Keep in mind that no one actually "obsoletes" anything. How we do
obsoleteness checking is simply to compare the time stamp serial numbers of
every unit withed transitively for every unit withed. They better all match.
Any mismatch is reported as an error, and the compilation aborted. And we of
course repeat the check when linking. We use the order only to provide
better error messages, and units are NEVER removed from the program library
(unless the programmer does so manually).

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

From: Tucker Taft
Sent: Tuesday, February  4, 2003  5:05 PM

> "Easy enough to check compatibility"? You've got to be kidding. The only
> thing that we know how to do with a symbol file is load them into a symbol
> table. But you can't do that to "check compatibility", because you've got to
> load the full view into the same place (can't have two packages with the
> same library-level name).

This is my whole point.  You *do* need to have both views available
at once, since one unit might have a "limited with" of package P,
while some unit that it depends on indirectly has a full "with" of P.
To avoid ripple effects, we want the one with the limited-with
to only see the limited view.

You mentioned in another note that you have separate symbol table
entries for various different forms of a type.  I think you will
need separate symbol table entries for the limited view and
the full view.  In other words, for a library package P, you really have two units,
one whose library-level name is "P-full" and one whose name is "P-limited".

> Keep in mind that no one actually "obsoletes" anything. How we do
> obsoleteness checking is simply to compare the time stamp serial numbers of
> every unit withed transitively for every unit withed. They better all match.

Yes, I understand that approach. I probably should have simply
talked in terms of bumping time stamps.  I think the same
point can be made in those terms.  We have a similar model,
albeit only in the program library we build up in memory.
[Since we allow the compiler to run for a "long" time and
to process different versions of the same file during a single
execution, we have all the obsoleteness checking mechanism
in there as well.]

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

From: Randy Brukardt
Sent: Tuesday, February  4, 2003  5:34 PM

> This is my whole point.  You *do* need to have both views available
> at once, since one unit might have a "limited with" of package P,
> while some unit that it depends on indirectly has a full "with" of P.
> To avoid ripple effects, we want the one with the limited-with
> to only see the limited view.

I don't see that. Each compilation has its own symbol table, and I don't see
any reason why the mere fact of withing something that saw a limited view of
some package was anything to do with a package that sees the full view.

> You mentioned in another note that you have separate symbol table
> entries for various different forms of a type.

No, "types" aren't in the symbol table at all. They have their own separate
table. Type names are in the symbol table, of course, as are component
names. But a type name has nothing to do with a type -- they're completely
separate concepts (as they are in Ada).

> I think you will
> need separate symbol table entries for the limited view and
> the full view.  In other words, for a library package P, you really have
> two units, one whose library-level name is "P-full" and one whose name
> is "P-limited".

If that's true, we're getting into horrific complexity territory. A
fundamental basis of the symbol table is that most entities have only one
name and cannot be overloaded. You're asking that all of the lookup code be
changed to be able to handle packages with two views. Along with all of the
declaration code (so it can write into the correct view). Moreover, that
would be true in every compiler (source based or library based).

If the proposal requires both copies in the symboltable, then I think the
proposal should be killed as soon as possible. If not sooner.

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

From: Pascal Leroy
Sent: Wednesday, February  5, 2003  3:40 AM

> I think you need to keep around indefinitely both a "full view" of
> the package and a "limited view" of the package.  Even after you
> do a "full" compile, some units can still do "limited with" and should only
> see the limited view.  What I would recommend is you treat them as
> pretty much distinct units.

In terms of language description, you're right, a unit that does a "limited
with" must see the limited view even for a unit that has been fully compiled.

However, the implementation you suggest might make sense for your compiler, it
doesn't make sense for ours (or for Randy's if I understand him right).  We
fundamentally depend on the invariant: 1 Ada unit = 1 Diana tree.  Changing this
is not an option.  This means that if we see "limited with P" and P has been
fully compiled, then we need to simulate/synthesize a limited view for P.

For name resolution, this is simple enough.  When we see the name P.T we do a
lookup of the string "T" in the identifier table for P.  The lookup has to
succeed if and only if T is part of the limited view.  We would probably do that
by examining the Diana tree for T.  Another option would be to store a bit "yes,
I am part of the limited view" on the defining identifier for T.

Once name resolution has been done, we will have access to the full tree for T.
At this point we will need to behave as if T was incomplete.  I can think of at
least 3 ways to achieve this in our compiler, but the simplest one is probably
to use a predicate "is this an incomplete type?" where appropriate, and have
that predicate determine whether visibility was obtained though a "limited
with".  We already have such a predicate, but I'm pretty sure that we don't call
it everywhere it would be needed.

This certainly looks like work, but we are not talking man-years here.  And
there is certainly no need to keep two trees for each unit.

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

From: Jean-Pierre Rosen
Sent: Wednesday, February  5, 2003  3:32 AM

> Add a legality rule:
>
> A library_item mentioned in a limited_with_clause shall be a
> package_declaration[, not a subprogram_declaration, generic_declaration,
> or generic_instantiation].

Why not generic_instantiation?
1) not really different from a package declaration
2) quite useful, since instantiations are commonly used for building objects with
   multiple facets.

Later you say:
>We do not allow a limited_with_clause to mention a generic
>instantiation, because that would require all kinds of semantic analysis
>stuff, such as visibility.
Well, the instantiation has been compiled at that point, so all visibility
should be solved.
Maybe worth some further investigation

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

From: Pascal Leroy
Sent: Wednesday, February  5, 2003  3:59 AM

The restriction is perfectly sensible.  We don't want to have to do any name
resolution to build the limited view, and in order to determine what generic we
are talking about, we would have to do a pretty extensive name resolution (use
clauses, parent units, renamings, etc.).  Moreover, in order to build the
instantiation, we would have to have compiled the specification of the generic
already, but limited views cannot have compilation prerequisites (if they had,
we could run into circularity problems).

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

From: Robert A. Duff
Sent: Wednesday, February  5, 2003  8:22 AM

Tucker and I discussed this issue yesterday, and we decided that
although it *is* troubling to make rules that cause package instances to
be different from normal packages, it is not feasible to implement the
proposal (in some compilers) without this restriction.

If there's a cycle, then the instantiation might *not* have been
compiled yet.  How about:

    limited with A;
    package B is new Some_Generic(...);

    limited with B;
    package A is new Some_Generic(...);

It cannot be the case that A and B have both been *fully* compiled
before each other.

I really think a key feature of this proposal is that when the compiler
sees "limited with X", it can determine the list of incomplete types in
X in a purely syntactic manner.  It should not have to do any kind of
heavy-duty semantic analysis.  It should not have to look at any source
text outside of X.

It's not *so* bad, because if you wanted to do cycles like the above,
you can always break the cycle by adding more generic formal parameters.

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

From: Jean-Pierre Rosen
Sent: Wednesday, February  5, 2003  11:28 AM

Yes. I see the problem, and I have sympathy for the poor compiler writers.

But with my teacher's hat on, I don't feel very easy to explain that in some
cases, an instantiation is not equivalent to a regular package.

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

From: Tucker Taft
Sent: Wednesday, February  5, 2003  5:47 AM

Pascal Leroy wrote:
>...

>
> From a pedagogical standpoint it would seem simpler to say that "limited
> with" works like "with", i.e. that "limited with P.C.D" is equivalent to
> "with P, P.C, P.C.D".  Any other option is going to unnecessary confuse
> users.
>
> So maybe the right answer is that if a package has a "limited with" of
> itself it has no effect (but is not illegal).

Yes, that is preferable.

> Yes, I was thinking that use clauses and package renamings would be OK in
> that context, although if they lead to problems we could disallow them.  But
> I can't think of any problem at this point.

I sent a note about this, titled "Restrictions on limited-with".
Have you seen it?  Any comments?
I think "use" clauses are a bad idea.
Renaming is tricky, and needs careful thought.

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

From: Pascal Leroy
Sent: Wednesday, February  5, 2003  6:23 AM

Yes, I saw it, but after sending the above message.  I agree that use clauses
cause a Beaujolais-ish effect (maybe this should be named the Chianti effect ;-)
and should be disallowed.  I also agree that renamings should probably be
disallowed since they cause more trouble than they are worth.

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

From: Robert Dewar
Sent: Wednesday, February  5, 2003  7:18 AM

So far it seems to me that the limited-with discussion looks like it
is very promising. I must say I was unhappy with the idea of furiously
trying to get a resolution in Padua (and I was trying to figure out
how to molest my schedule to attend :-) but now I am actually getting
some confidence that

a) this is going in the right direction
b) there really is a good possibility of agreement

Very encouraging :-)

We will definitely try to prototype this in GNAT as soon as there is a
reasonably well defined proposal.

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


Questions? Ask the ACAA Technical Agent