Version 1.8 of ais/ai-60217.txt

Unformatted version of ais/ai-60217.txt version 1.8
Other versions for file ais/ai-60217.txt

!standard 03.10.01 (02)          03-05-16 AI95-00217-07/02
!standard 03.10.01 (03)
!standard 03.10.01 (11)
!standard 08.03.00 (19)
!standard 08.05.03 (04)
!class amendment 03-03-03
!status No Action (10-0-0) 03-10-03
!status work item 03-03-03
!status received 03-03-03
!priority High
!difficulty Hard
!subject Incomplete type completed in a child
!summary
A new form of incomplete type declaration is provided for specifying that a type is to be completed in a child or nested package. This enables a set of mutually dependent type definitions to cross packages that are part of a package hierarchy.
!problem
Ada 95 does not allow two package specs to depend on each other. This can cause problems when creating Ada packages to use in interfacing with code written in languages like Java. In Java, there are no restrictions against mutually dependent class definitinos. When expressed in Ada, this means that a package may define a type which is used in a second package's spec, while also making use of a type defined in that package spec. This is clearly not allowed due to restrictions against cyclic semantic dependences. Cyclic semantic dependences cannot generally be allowed in Ada because of the order of elaboration requirements.
This problem also can occur when organizing large object-oriented systems solely in Ada 95. The "solution" is to place all of the types in a single package. However, the rules for defining primitive operations of a type require that most of the operations of the type also must be declared in the package. This leads to gigantic packages. An alternative solution is to add additional indirection into the data structures. This adds complexity as well as space and time costs at runtime.
!proposal
(See wording.)
!wording
Change paragraphs 3.10.1(2,3) as follows ("{}" are additions, "[]" are deletions, except in the syntax part):
incomplete_type_declaration ::= type [package_identifier.]defining_identifier[discriminant_part] [is tagged];
An incomplete_type_declaration requires a completion, which shall be a full_type_declaration {, a private_type_declaration, or a private_extension_declaration}. {If a package_identifier is present in the declaration, the completion shall occur immediately within the visible part of a package with this identifier, which shall be declared later and immediately within the innermost enclosing declarative region. Otherwise, if}[If] the incomplete_type_declaration occurs immediately within ...
< rest remains the same >.
Replace 3.10.1(10) with the following:
A dereference (implicit or explicit) of a value of an access type whose designated type D is incomplete is allowed only in the following contexts:
* in a place where the completion of D is visible;
* in a context where the expected type is E and
o E covers the completion of D, o E is tagged and covers D, o E covers D'Class or its completion, or o E'Class covers D or its completion;
* as the target of an assignment_statement where the type of the value
being assigned is V, and V or V'Class is the completion of D.
In these contexts, the incomplete type is defined to be the same type as its completion, and its first subtype statically matches the first subtype of its completion.
Add after the above:
Post-Compilation Rules
If a package identifier is present in the declaration, and the declaration occurs immediately within the visible or private part of an enclosing package, and the named package is not itself declared later and immediately within the same part of the enclosing package, then the enclosing package shall be a library unit, and the named package shall be a child of this library package (see 10.1.1). Furthermore, if the incomplete type is declared in the visible part, then the named package shall be a public child. In either case, the named child package is needed in a partition (see 10.2) that includes the enclosing library package.
Add the following sentences to the end of paragraph 3.10.1(11):
If a package_identifier is present in the declaration, the incomplete type is declared immediately within the declarative region of the named package. Otherwise, the incomplete type is declared immediately within the innermost enclosing declarative region.
Add the following paragraph after 3.10.1(11):
If places where one or more incomplete_type_declarations are visible which include the same package_identifier, a view of the named package is visible with this identifier. The view includes only the incomplete types named in these incomplete_type_declarations. [Note that in places where the package_declaration or a library_unit_renaming_declaration for this package is visible, all of the incomplete_type_declarations will be hidden from all visibility.] AARM only: Proof: 8.3(19) <as revised below> states that
a visible completion hides from all visibility the prior declaration. 8.5.3(4) <as revised below> indicates that the view provided by a library item package renaming includes at least the entire visible part, meaning that all of the completions will be visible.
Change 8.3(19) as follows:
If the completion of a declaration is a declaration, then [within the scope of] {in places where} the completion {is visible}, the first declaration [is] {, and any declarations it completes, are} hidden from all visibility. Similarly, a discriminant_specification or parameter_specification is hidden [within the scope of] {in places where} a corresponding discriminant_specification or parameter_specification of a corresponding completion, or of a corresponding accept_statement {is visible}.
Add the following after 8.5.3(4):
A view of a package can be visible due to any of the following kinds of declarations being visible:
1) A package_declaration (see 7.1); 2) An incomplete_type_declaration that mentions a package (see 3.10.1); 3) A package_renaming_declaration that is a library item (see 10.1.1); 4) A package_renaming_declaration that is not a library item.
The view provided by a name that denotes a package_renaming_declaration is determined by what if any other declarations are visible that provide a view of the same package, as determined by the first of the following that applies:
1) If the package_declaration itself is visible, then the view
provided is the same as that provided by a name that denotes the package_declaration;
2) Otherwise, if only package_renaming_declarations
are visible, or if at least one of the renamings is a library item, then the view provided is that of the visible part of the package, augmented with whatever public children of the package have been mentioned in with clauses that are in scope;
3) If neither of the above, then the view provided is that determined
by the set of incomplete_type_declarations that are visible and that mention the package.
!example
(a) Here is the classic case of mutual dependence, where an employee belongs to a particular department, and a department has a manager who is an employee. (We assume the use of tagged types here to illustrate the use of tagged incomplete types.)
-----------------
package Office is type Departments.Department is tagged; type Dept_Ptr is access all Department'Class; end Office;
package Office.Employees is type Employee is tagged private; type Emp_Ptr is access all Employee'CLass; procedure Assign_Employee(E : in out Employee; D : in out Departments.Department); ... function Current_Department(D : in Employee) return Dept_Ptr; end Office.Employees;
with Office.Employees; package Office.Departments is type Department is tagged private; subtype Dept_Ptr is Office.Dept_Ptr; procedure Choose_Manager(D : in out Department; Manager : in out Employees.Employee); ... end Office.Departments;
--------------
(b) If it is desirable that Employees and Departments be top-level library packages, the following renames can be provided:
with Office.Employees; package Employees renames Office.Employees;
with Office.Departments; package Departments renames Office.Departments;
----------
(c) Alternatively, there is really no need for Employees to be a child in the above example, so it can be defined directly as package "Employees" rather than as package "Office.Employees," eliminating the need for the first renaming. In this case, package "Office" might better be called something else, since it no longer encapsulates the entire "office" abstraction. E.g., it might be called simply "Forward":
package Forward is -- "Forward" declarations of types that complete a cycle type Departments.Department is tagged; type Dept_Ptr is access all Department'Class; ... -- and possibly other types needed to break a cycle end Forward;
with Forward; use Forward; package Employees is type Employee is tagged private; type Emp_Ptr is access all Employee'CLass; procedure Assign_Employee(E : in out Employee; D : in out Departments.Department); ... function Current_Department(D : in Employee) return Dept_Ptr; end Employees;
with Employees; package Forward.Departments is type Department is tagged private; subtype Dept_Ptr is Forward.Dept_Ptr; procedure Choose_Manager(D : in out Department; Manager : in out Employees.Employee); ... end Forward.Departments;
with Forward.Departments; package Departments renames Forward.Departments;
------------------- (d) One option not fully analyzed would be to allow the incomplete type to be completed by a type within a package renaming. This might require somewhat different wording changes, but this is how the above example would look. Note that now the packages are both defined as top-level library packages, and the only child unit is a renaming:
package Forward is -- "Forward" declarations of types that complete a cycle type Departments.Department is tagged; type Dept_Ptr is access all Department'Class; ... -- and possibly other types needed to break a cycle end Forward;
with Forward; use Forward; package Employees is type Employee is tagged private; type Emp_Ptr is access all Employee'CLass; procedure Assign_Employee(E : in out Employee; D : in out Departments.Department); ... function Current_Department(D : in Employee) return Dept_Ptr; end Employees;
with Forward; with Employees; package Departments is type Department is tagged private; subtype Dept_Ptr is Forward.Dept_Ptr; procedure Choose_Manager(D : in out Department; Manager : in out Employees.Employee); ... end Departments;
with Departments; package Forward.Departments renames Departments; -- complete the type via a renaming (possible option)
!discussion
This proposal attempts to address the need to have types that are mutually dependent which are not all defined in the same library package. The approach is built upon the idea that any group of types that are mutually dependent are necessarily part of a single logical abstraction, and hence will often be declared as part of a package hierarchy. We take advantage of this by placing the incomplete type declaration in the root package of the abstraction, and then the types that are mutually dependent can be declared in this same package, or in its children.
The proposed syntax is intended to be analogous to the syntax used for declaring child units, and in fact was discussed during the Ada 9X design process as a natural extension of deferred incomplete types. The problem in the Ada 9X process was we only considered allowing the declaration in the private part of the parent. That, unfortunately, doesn't solve the mutually dependent type problem. By allowing this generalized incomplete type declaration in the visible part of the parent, we have completed the process of allowing a large abstraction to be spread across multiple packages in a hierarchy.
In fact, the proposal is not restricted to packages in a hierarchy, though that is one anticipated style of usage. Only the package that breaks the cycle need be declared as a child. All the other packages can be declared as top-level library units, as illustrated in the first "package Forward" example above (example (c)).
Note that this proposal allows the completing type to be defined in an instantiation. The second "package Forward" example (example (d)) illustrates a further option, where we would allow the type to be "completed" in a renaming. Although this option has not been fully investigated, it has the interesting effect of allowing all the package declarations to be top-level, and only the renaming is a child. This option might resolve some of the concerns about being forced to declare one or more packages as children.
The wording change to 8.3(19) deserves some extra discussion. The change from "within the scope" to "places where visible" seems necessary for any of our proposals, to avoid the "ripple" effect where an indirect "with" dependence can have significant effects on a unit outside the scope of the "with" clause.
The original "within scope" wording would pull completions into all semantic dependents of the completing package, since the scope of a library item extends to all its dependents, including those units that do not have "with" visibility on the _item. But we have agreed in past discussions that we want the completing package (or a library rename thereof) to be visible, not just somewhere within scope, if we are going to consider the type "completed." The new wording of the paragraph is intended to have no effect on existing code.
Note the addition to 8.5.3. We want non-library-unit renamings to provide the same view as whatever other view we already have of a package, whereas an explicit "with" of a library-unit renaming should always show at least the entire visible part. We believe that this revision of the wording accomplishes this (albeit a bit wordily). Basically, we want one and only one view of a package at any given place, no matter what name is used to identify the package.
One note about the syntax, to address Randy's concern. Adding the optional "[package_identifer.]" to the syntax could be treated by the parser roughly in the same way as the optional "[parent_unit_name.]" is treated in section 6.1 for defining_unit_name. You could allow this prefix on any type declaration, and then have a semantic rule that makes sure it is only used for incomplete type declarations.
To summarize, this proposal is intended to involve the smallest change to the RM wording and to most implementations, and with the use of one package renaming as illustrated in the "package Forward" examples ((c) and (d)), it can accommodate use of the feature for packages that are not all part of a single package hierarchy.
!ACATS test
!appendix

Editor's note: Early discussion about this proposal can be found in
AI-217-05.

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

From: Tucker Taft
Sent: Thursday, February 13, 2003  2:48 PM

I was tasked with writing up the "type C.T;" proposal.  Here it is. [Editor's
note: This version of the AI was not posted; version /01 is similar and more
complete.] One interesting option presented itself as I was working on it. If
we permit these incomplete types to be completed by a renaming of a package,
then the packages participating in the cycle can all be declared as top-level
library units.  See example (d) in the AI for how this might look.  I think
this paradigm might alleviate the concern about being forced to use child
units.

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

From: Tucker Taft
Sent: Tuesday, March 11, 2003  4:57 PM

Randy asked me to submit this one again, because
it wasn't complete.  In particular, it lacked rules
on when A.all := B.all would be legal, and it didn't
have any post-compilation rules regarding when a child
package was "needed," etc. [Editor's note: This is version /01 of the AI.]

Also, it is really variant "7" rather than "VIII" (sorry about
that, can't imagine how I lost count ;-

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

From: Pascal Leroy
Sent: Wednesday, March 12, 2003  9:29 AM

As I am sure you realize, this AI has nothing to alleviate my concerns regarding
the facts that (1) an implementation needs to juggle with both views of a unit
at the same time and (2) addition of package renamings may cause existing names
to become illegal.  Regardless of aesthetical preferences (and I still find
"type C.T" distasteful) these are very serious implementation concerns to me.

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

From: Tucker Taft
Sent: Wednesday, March 12, 2003  1:52 PM

I presume the "limited with" proposal will address these issues ;-).
I am happy to update the -07 variant if you come up with
a nice solution that works for it as well.

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

From: Pascal Leroy
Sent: Wednesday, March 12, 2003  9:29 AM

It may be that the "simplest" solution is to alter proposed wording for 3.10.1(10)
and 8.3(19), so that instead of saying simply "the completion is visible",
we say "the completion is visible (other than via a renaming that is
not a library unit renaming)."

There might be a less painful way to say it.  Perhaps something like
"where the completion is visible and, furthermore, that is within the library
unit enclosing the completion or within the scope of a 'with' clause that
mentions this library unit or a library unit renaming thereof."

Still pretty painful...

The level of wording pain may not matter that much, if we think this will
work in the obvious way for normal cases.  I understand the goal
of keeping it as straightforward to implement as possible.
We are only dealing with cases where both the incomplete type and the
completion are in scope, but by different fully expanded names,
which has got to be rare.

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

From: Randy Brukardt
Sent: Saturday, March 22, 2003  12:15 AM


This example shows two ways that I would use alternative #7 of AI-217
in the Claw Builder. See the similar write-up on alternative #5 for details
about the Builder and why it is important to be able to use this facility in
it.

[Editor's note: Here are those details.]

The Claw Builder is a real, existing program which could use this facility.
The problem is that some objects need references to other types of objects,
and these needs are circular. For instance, (some) types of window objects
include menu objects. And some types of menu objects include actions that
open a window.

The current Claw Builder solves this problem by using names rather than
access objects to connect the objects in some cases. This is usually done
only where necessary to break circularities. For instance, menu objects name
the windows they are to open, rather than linking to them. Using names
causes several problems:
   -- Accesses to the linked object is much slower, as they have to be
      looked up by name before use;
   -- If the user renames the linked object, we have to walk the entire
      project to insure any names are updated;
   -- If the user copies the linked object and then renames the copy (the
      required behavior), we have to be careful NOT to walk the project
      and update names -- harming code reuse.
   -- We can't have overloaded names (not a problem for windows, but can
      happen in other cases).

A root window object is an abstract object with a fairly large set of
operations. Each concrete object has to provide implementations for many of
these operations (some it can inherit). All of these operations are
dispatching. Typically, a user of the operation would apply it to a list of
windows using an iterator generic, with the operation dispatching to the
correct implementation. For the purposes of this discussion, we'll look at
just a few: Show, Hide, Display_Name.

The existing package looks something like:

   with CBuild_Menu;
   package CBuild_Root is
      type Root_Window_Type is abstract new
	    Ada.Finalization.Limited_Controlled with private;
      type Any_Window_Access_Type is access all Root_Window_Type'Class;

      procedure Show (Window : in out Root_Window_Type) is abstract;
      procedure Hide (Window : in out Root_Window_Type) is abstract;
      function Display_Name (Window : in Root_Window_Type)
          return String is abstract;
      ...
   private
      type Root_Window_Type is abstract new
	    Ada.Finalization.Limited_Controlled with record
          ... CBuild_Menu.Something ...
      end record;
   end CBuild_Root;

The menu package looks something like (greatly simplified):

  with CBuild_Id_Type;
  package CBuild_Menu is
    type Menu_Item is record
       Name : String (1..20);
       Action : Action_Type;
       Dialog : CBuild_Id_Type; -- The name of a dialog window.
           -- If Action=Open_Dialog.
    end record;
    procedure Simulate_Action (Item : in Menu_Item);
  end CBuild_Menu;

  with CBuild_Root, CBuild_Lists;
  package body CBuild_Menu is
    procedure Simulate_Action (Item : in Menu_Item) is
    begin
       if Item.Action = No_Action then
           null;
       elsif Item.Action = Open_Dialog then
           CBuild_Root.Show (
                 CBuild_Lists.Lookup(Item.Dialog,
                     CBuild_Data.Top_Level_Window_List));
       ... -- Other actions.
       end if;
    end Simulate_Action;
  end CBuild_Menu;

[End details copied from alternative #5.]

Alternative #7 requires the use of a child package to hold the main
definition. Thus, we have to add a parent whose primary purpose is to be the
abstract. (Again we're assuming that AI-326 is approved.)

   package CBuild_Definitions is
      type Root.Root_Window_Type is tagged;
      type Any_Window_Access_Type is access all Root_Window_Type'Class;
   end CBuild_Definitions;

This parent package would be used directly when you need to declare
instances (usually components) of the type in locations where the reference
would be circular.

In order to keep the use of the access types compatible with the existing
definition, we'll need to subtype the access type in the main package. And
of course, it has to be the child package named.

   with CBuild_Menu;
   package CBuild_Definitions.Root is
      type Root_Window_Type is abstract new
	    Ada.Finalization.Limited_Controlled with private;
      subtype Any_Window_Access_Type is
CBuild_Definitions.Any_Window_Access_Type;

      procedure Show (Window : in out Root_Window_Type) is abstract;
      procedure Hide (Window : in out Root_Window_Type) is abstract;
      function Display_Name (Window : in Root_Window_Type)
          return String is abstract;
      ...
   private
      type Root_Window_Type is abstract new
	    Ada.Finalization.Limited_Controlled with record
          ... CBuild_Menu.Something ...
      end record;
   end CBuild_Definitions.Root;

In order to avoid having to change every existing use of the package, we'd
need to define a library level rename:

    with CBuild_Definitions.Root;
    package CBuild_Root renames CBuild_Definitions.Root;

The client package (in this case, for menus) would look something like:

  with CBuild_Definitions;
  package CBuild_Menu is
    type Menu_Item is record
       Name : String (1..20);
       Action : Action_Type;
       Dialog : CBuild_Definitions.Any_Window_Access_Type;
           -- If Action=Open_Dialog.
    end record;
    procedure Simulate_Action (Item : in Menu_Item);
  end CBuild_Menu;

  with CBuild_Root;
  package body CBuild_Menu is
    procedure Simulate_Action (Item : in Menu_Item) is
    begin
       if Item.Action = No_Action then
           null;
       elsif Item.Action = Open_Dialog then
           CBuild_Root.Show (Item.Dialog.all);
       ... -- Other actions.
       end if;
    end Simulate_Action;
  end CBuild_Menu;

The reasons I didn't like the first solution in my last note hold here, too:
it's not clear when to use the definitions package, and when to use the main
package. It would be preferable to keep an understandable separation between
them.

As before, we could create a "client" package and a "creator" package. The
"client" package  would be used by ordinary client packages of the
abstraction. The "creator" package would be used only by packages that need
to create new extensions of the type or create objects of the type. The
"creator" package would, of course, be similar to the existing package. The
"client" package would provide enough operations that regular clients could
use it only.

Since the "client" package would be used most frequently, I've given it the
existing name, and thus it would be the parent package:

   package CBuild_Root is
      type Creator.Root_Window_Type is tagged;
      type Any_Window_Access_Type is access all Root_Window_Type'Class;

      procedure Show (Window : in out Root_Window_Type'Class);
      procedure Hide (Window : in out Root_Window_Type'Class);
      function Display_Name (Window : in Root_Window_Type'Class)
          return String;
      ...
   end CBuild_Root;

The "creator" package would be as in the previous example (except for the
name):

   with CBuild_Menu;
   package CBuild_Root.Creator is
      type Root_Window_Type is abstract new
	    Ada.Finalization.Limited_Controlled with private;
      subtype Any_Window_Access_Type is CBuild_Root.Any_Window_Access_Type;

      procedure Show (Window : in out Root_Window_Type) is abstract;
      procedure Hide (Window : in out Root_Window_Type) is abstract;
      function Display_Name (Window : in Root_Window_Type)
          return String is abstract;
      ...
   private
      type Root_Window_Type is abstract new
	    Ada.Finalization.Limited_Controlled with record
          ... CBuild_Menu.Something ...
      end record;
   end CBuild_Root.Creator;

The body of CBuild_Root would need visibility on the completing type so that
it
could implement the procedures, meaning it would have to with the Creator
package:

   with CBuild_Root.Creator;
   package body CBuild_Root is
      procedure Show (Window : in out Root_Window_Type'Class) is
      begin
          CBuild_Root_Definition.Show (Window);
      end Show;
      procedure Hide (Window : in out Root_Window_Type'Class) is
      begin
          CBuild_Root_Definition.Hide (Window);
      end Hide;
      function Display_Name (Window : in Root_Window_Type'Class)
          return String is
      begin
          return CBuild_Root_Definition.Display_Name (Window);
      end Display_Name;
      ...
   end CBuild_Root;

As before, with this structure, the clients with CBuild_Root, and the
concrete object packages with (or are children of) CBuild_Root_Definition.
The two packages have clearly defined roles.

For instance, the menu case would look something like:

  with CBuild_Root;
  package CBuild_Menu is
    type Menu_Item is record
       Name : String (1..20);
       Action : Action_Type;
       Dialog : CBuild_Root.Any_Window_Access_Type;
            -- If Action=Open_Dialog.
    end record;
    procedure Simulate_Action (Item : in Menu_Item);
  end CBuild_Menu;

  package body CBuild_Menu is
    procedure Simulate_Action (Item : in Menu_Item) is
    begin
       if Item.Action = No_Action then
           null;
       elsif Item.Action = Open_Dialog then
           CBuild_Root.Show (Item.Dialog.all);
       ... -- Other actions.
       end if;
    end Simulate_Action;
  end CBuild_Menu;

which is unchanged from the alternative #5 version.

---

Of course, the best way to avoid the issue of deciding when to use one of
the two packages is to avoid having the second one in the first place.
Unfortunately, this alternative (unlike the other two) does not allow that.
The restrictions on the placement of the stub mean that there is no way to
avoid having to 'with' two different packages in the specification and body
of CBuild_Menu.

The closest we can come would be to wrap the entire program in a parent
package. Then we could put the stub into that wrapper, and no with would be
needed to access it. Of course, the with is still there, the only difference
is that it is implicit. Moreover, this would require changing the
declaration of every compilation unit in the program (there's now over 100
units in the Claw Builder) - and, if we continue to follow our coding
standard, most of the uses as well.

There is an even more serious problem with this proposal, which is that it
isn't responsive to the SIGAda concerns. The dependence implied in the stub
definition is not natural (parents do not usually know about their children)
and is well hidden deep in the package specification, not clearly specified
in the context clause.

That isn't obvious in these examples, but would be if the many lines of
header comments and change log entries were included before the start of
declarations in the package. A typical package has a 25-line copyright
message, the context clause and package header, then a description of the
program, a description of the function of the package, and an edit history,
before any declarations.

I'll have more to say about this sometime when I'm awake.


That concludes my look at using alternative #7 in CBuild. Coming soon: a
similar look at alternative #6.

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

From: Tucker Taft
Sent: Wednesday, March 26, 2003  1:53 PM

Randy said:

> type C.T does not meet the SigAda criteria IMO, and I'm thinking about it as
> little as possible. I'd motion to kill it, but I'll have to wait until June
> to do that.

I guess I object to this attitude.  I think the "SIGAda"
participants did not consider this proposal, so it is
hard to say whether they would consider a stub in
a parent adequate "notice" of the existence of a
dependence.  In my view, it corresponds quite similarly
to the "notice" provided for subunits.

In any case, I will try to present the three alternatives
to AdaUK in a couple of weeks, and see if we can get
some more helpful user feedback.  I would encourage
Pascal to produce something soon, even if it lacks
detailed wording, so long as it captures his intent
in a way that others can review it.

Many of us still believe this is one of the most
important Ada 200Y issues.

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

From: Dan Eilers
Sent: Wednesday, March 26, 2003  2:09 PM

>              In my view, it corresponds quite similarly
> to the "notice" provided for subunits.

So why not just use subunits as the mechanism?
The notice provided by subunit stubs is presumably adequate.
And you can use the "limited with" syntax to indicate
"with"ing a unit without creating a semantic dependence
on the subunits declared in its spec.

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

From: Tucker Taft
Sent: Thursday, March 27, 2003  5:42 AM

> So why not just use subunits as the mechanism?
> The notice provided by subunit stubs is presumably adequate.
> And you can use the "limited with" syntax to indicate
> "with"ing a unit without creating a semantic dependence
> on the subunits declared in its spec.

First, existing subunits are defined semantically by
being substituted at the point of the stub.  That is
exactly *not* what we want to do.

Secondly, I tend to agree with Randy that any proposal
that requires both a new kind of context clause and a
new kind of incomplete type stub seems more complex
for the user than a proposal that requires only one or
the other.  "Limited with" seems to have a lock on
the best solution that requires only a context clause.

For the stub-only alternative, we need to find an incomplete
type stub that is considered by users to be sufficiently
visible and well-defined on its own to not require a
special context clause as well.  My view is that the
incomplete child type proposal does that.  But that is
just my view, and it is clear that at least Randy either
doesn't like its syntax and/or its reliance on the child
relation, or believes that there is no incomplete type
stub of any syntax or semantics that would pass muster
with users unless it has a special context clause as well.

One possibility it to try to make the incomplete child
type stub more "visible" by adding more reserved words,
such as "separate."  Another is to move the incomplete
child type stub into the context clause, or require it
precede all other declarations inside the parent.
But none of these address the other objection to being restricted
to the child relation.  I find that restriction natural, others
don't.  And sometimes it is hard to separate negative
reactions to syntax from negative reactions to semantics.

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

From: Dan Eilers
Sent: Friday, March 28, 2003 11:38 AM

> First, existing subunits are defined semantically by
> being substituted at the point of the stub.  That is
> exactly *not* what we want to do.

But it is exactly what we normally want to do.  Normal clients
want to see the full abstraction, including the completions
of the incomplete types.  And they want to see the full abstraction
without having to "with" (or rename) a child unit.
Only clients involved in the circular dependency need a restricted
view of the abstraction (to break the circularity), and for that I
proposed using the "limited with" (or similar) syntax.

I agree that using child units avoids having to add new syntax
for "with" clauses (since the client can "with" the parent to see
the restricted view, or the child to see the full view).  But this
doesn't seem advantageous to the user, since almost all clients
want to see the full view, and are stuck having to "with" (or rename)
the child.

The other differences between a subunit solution and a child-unit
solution all seem to favor subunits:
  1) the stub of a subunit naturally provides the ideal level of
     notice of a forthcoming semantic dependency;
  2) parent units depending on subunits is natural, but
     parent units depending on child units is unnatural;
  3) subunits allow an abstraction (package spec) to be divided
     into separately-compiled pieces while preventing a client
     from directly "with"ing the pieces containing implementation
     details that are intended to remain hidden.
  4) allowing subunit stubs in package specs amounts to removing
     an unnecessary restriction from the language, but allowing
     dependencies from a package spec to a child unit amounts to
     adding a new feature.

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

From: Randy Brukardt
Sent: Friday, March 28, 2003  7:34 PM

I have to agree with Dan here. It seems that a subunit-based solution might
be a better approach than alternative 7 ("type C.T;"), because subunits seem
more natural than child units for this purpose.

However, any solution that requires writing two things is going to be
inferior to a solution that requires writing one. So, I suspect that
alternative 6 ("limited with") is likely to carry the day based on better
usability, even if it gives some implementers heartburn. Of course, if a
semantic or implementation showstopper is uncovered, we'll end up having to
revert to one of the other proposals, and in that case, it would be valuable
to work out a solution based on subunits.

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

From: Randy Brukardt
Sent: Wednesday, March 26, 2003  2:45 PM

> I guess I object to this attitude.  I think the "SIGAda"
> participants did not consider this proposal, so it is
> hard to say whether they would consider a stub in
> a parent adequate "notice" of the existence of a
> dependence.  In my view, it corresponds quite similarly
> to the "notice" provided for subunits.

But the same argument applies to the type stubs that you presented at
SIGAda, and we know for certain that those were not acceptable to those
participants.

There is no natural dependence from a parent to a child, as opposed to from
a spec to body or a child to a parent.

> In any case, I will try to present the three alternatives
> to AdaUK in a couple of weeks, and see if we can get
> some more helpful user feedback.

I would feel a lot better if an unbiased observer did the presenting at
AdaUK. The Padua minutes say that John Barnes was going to do that. I know
that the biases of the presenter can make a substantial impact on the
results of a discussion (that certainly happened in Padua on this very
discussion), and I fear that that will color the feedback that we get.

> I would encourage Pascal to produce something soon, even if it lacks
> detailed wording, so long as it captures his intent
> in a way that others can review it.

Which points out my other concern. There is absolutely no doubt that
'limited with' is the best proposal from a usability standpoint - there's
less syntax and it fits well with the existing Ada model. But we don't know
at this point if we can come up with a consistent set of rules for it, or it
is even implementable. I fear that we may be 'poisoning the well' if we
present this as a possibility now, and then are forced to give up on it in
the future.

In any case, it would be good if you would send out your examples in time
for the rest of us to review them (as agreed in Padua). I've sent out mine
for this very reason.

> Many of us still believe this is one of the most important Ada 200Y
issues.

I still believe that, too. I'm just convinced that it is going to take two
years to come up with an finished proposal for 'limited with' (because
futzing with visibility rules requires extreme care), and we're going to
have to keep at least one of the others open until we can do that. And I
know I can't wait two more years to produce a meaningful upgrade to the Claw
Builder, so I'm just going to have to implement something. And once I do
that, there is little reason to change to something else (the same problem
ACT has). So any decision here is going to be too little, too late.

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

From: Robert Dewar
Sent: Wednesday, March 26, 2003  4:57 PM

> I guess I object to this attitude.  I think the "SIGAda"
> participants did not consider this proposal, so it is
> hard to say whether they would consider a stub in
> a parent adequate "notice" of the existence of a
> dependence.  In my view, it corresponds quite similarly
> to the "notice" provided for subunits.

I agree with Tuck, SIGAda is simply a source of input, not some official body
with veto power, especially veto power over something they have not seen!

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

From: Robert Dewar
Sent: Wednesday, March 26, 2003  5:00 PM

> But the same argument applies to the type stubs that you presented at
> SIGAda, and we know for certain that those were not acceptable to those
> participants.

Even that is too strong. The folks at SIGAda did not do a careful analysis of
the semantic issues, they simply expressed some wishes. It is fine to take the
wishes into effect, but they should not be taken as vetoes. I think at this
stage that the ARG is quite capable of making up its mind on the approach to
follow, taking into account the input from SIGAda, but only taking it into
account, not regarding it as proscriptive.

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

From: Randy Brukardt
Sent: Wednesday, March 26, 2003  8:54 PM

Fine. But we discussed the SIGAda results extensively at Padua, and we
agreed there that those concerns had to be addressed. (Thus the mildly
clunky alternative 5 of type stubs). If we're going to ignore ARG decisions
of less than 6 weeks ago, then I don't see how we're ever going to come to
any conclusions on this topic. Because every time we reopen an issue, we get
two new ideas to work out along with the (now 7) existing ones.

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

From: Tucker Taft
Sent: Wednesday, March 26, 2003  9:17 PM

In Padua I believe the ARG decided to pursue
all three of the proposals (incomplete in child,
type stub, and limited with). I don't see
anything that has changed that. I realize
not everyone liked all of the proposals, but
at least initially we decided to consider them
all on an even footing.  I did my wording
assignment at least. ;-)

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

From: Robert Dewar
Sent: Wednesday, March 26, 2003  9:34 PM

It's always nice if you can work with incremental decisions, but in a case like
this, I suspect the whole thing has to be evaluated in final form as a package.

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

From: Randy Brukardt
Sent: Wednesday, March 26, 2003  10:07 PM

I agree that was the final conclusion. But that is completely inconsistent
with the smaller decisions made during the meeting. I don't know anymore
what to make of them - perhaps we should just forget that four hours of
discussion?

Clearly, if we're just going to ignore those decisions, then I should make a
realistic proposal based on the original type stubs. By following those
decisions carefully, alternative 5 is not as clean or as usable as the
original proposal.

Now, what I see is that another alternative is essentially ignoring one of
those decisions. That necessarily makes that alternative look 'cleaner' than
the other ones which follow those decisions. And the reason given applies
equally to the alternative 4 which we killed.

That essentially makes the results of the 'users' at AdaUK a forgone
conclusion. *I* don't like the way type stub looks in the examples *I*
wrote -- who else is going to??? And *of course* type C.T looks better -- it
doesn't meet a number of the criteria, so of course its simpler to look at.

It's abundantly clear to me that I've been wasting my time on this issue -
we're comparing apples to oranges. I can only hope that Pascal can make
semantic sense out of 'limited with', because otherwise this problem will
never be solved.

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

From: Tucker Taft
Sent: Thursday, March 27, 2003  5:26 AM

> I agree that was the final conclusion. But that is completely inconsistent
> with the smaller decisions made during the meeting. I don't know anymore
> what to make of them - perhaps we should just forget that four hours of
> discussion?

I can accept that some people felt that the incomplete child type
suffered from the same problem as the original type stub, in
that it was not sufficiently visible and needed something in the
context clause.  But not everyone felt that way, and for whatever reason,
the decision was to pursue three proposals: limited with,
type stub with context clause, and incomplete child type
(without context clause).

I promise that I am not trying to annoy you ;-).
I'm just asking that you not prejudge the other proposals.
I realize you have your own preferences.  That is natural,
and that makes it harder to act also in the role of RM/AI editor.
But you have generally been quite successful in the past
in wearing multiple hats, and I am only asking you to try
to continue that semi-split-personality on this one.
I realize it may be frustrating at times if you personally think
we are going down a wrong path.

> Clearly, if we're just going to ignore those decisions, then I should make a
> realistic proposal based on the original type stubs. By following those
> decisions carefully, alternative 5 is not as clean or as usable as the
> original proposal.
>
> Now, what I see is that another alternative is essentially ignoring one of
> those decisions.

The ARG conclusion was to pursue the incomplete child type
without a context clause, for whatever reason.  I don't feel
I am ignoring any decisions in doing so.  I realize it is
possible to generalize the requirement imposed on the type
stub to have a context clause, to apply it to the incomplete
child type.  But at least there are some people who see the
type stub and the incomplete child type as significantly
different proposals, without having exactly the same issues.
In particular, the type stub refers to an unrelated package,
whereas the incomplete child type refers to a child/nested package.
That distinction is sufficient for some people to reduce
the need for context-clause visibility, and not for others.

> ...
> It's abundantly clear to me that I've been wasting my time on this issue -
> we're comparing apples to oranges. ...

You are definitely not wasting your time.  You have provided
by far the best examples.  I realize it is frustrating if the
examples make you believe there are other better ways of
solving the problem, but at least for now, the ARG has chosen
to pursue just these three, to try to keep us all sane.

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

From: Tucker Taft
Sent: Thursday, May 15, 2003  8:37 AM

As you may have noticed, the AdaUK folks showed a surprisingly
strong preference for the "type C.T" approach, followed by
the "limited with."  Since I presented the material, part of
this might be that my bias showed through, though I tried to
be impartial (other observers there, such as John Barnes, might
be able to comment on my success), and frankly, I like the
"limited with" proposal from a user point of view.

Some of the reasons cited for preferring the type C.T approach were
that it seemed to be a smaller change that was easier to understand,
and that it didn't introduce any new kinds of module interdependence.
It also seemed to be more "structured" to some members of the audience.

For what that's worth...

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

From: Pascal Leroy
Sent: Thursday, May 15, 2003  10:15 AM

Yes, I noticed the results of the votes; but even so I still think
strongly that "type C.T" is misguided.

When explaining the (numerous) proposals that we considered for solving
the mutually-dependent types problem, I have often felt a lot of
discomfort, no matter what proposal was presented.  It seems to me that
the notions of using something before having declared it, of naming a
compilation unit without having a semantic dependence on it, etc., are
extremely unpalatable to Ada users.  Of course we know that, whatever
the details, any solution will have these properties, but the average
Ada programmer has the feeling of somehow losing the safety of the
language.  I believe that this explains for instance the request at
SIGAda of naming the unit being forward-referenced in the context
clause.

It's quite difficult to judge a proposed language feature after a 15-mn
presentation, in particular because there is a tendency to draw
analogies with existing features, and such analogies may be invalid.  I
remember that the first time I was told about child units, I was shocked
that the parent didn't have to declare their children somehow.  Of
course I realize now that this would annihilate the benefits of child
units for extensibility, but it took me some time to swallow that model.

From that perspective, type C.T is certainly easier to swallow as it is
an extension of incomplete types completed in the body, and "limited
with" looks odd with its partial compilation mechanism.  But I think
that, when people have gotten used to it, they will be happier with
"limited with" than with "type C.T".

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

From: Arnaud Charlet
Sent: Friday, May 16, 2003  10:30 AM

I strongly agree with Pascal here, for the same reasons.
Also since I was at the AdaUK meeting, I would say that indeed the
three proposals were presented relatively rapidly (and there was clearly
many other topics to cover).

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

From: Robert Dewar
Sent: Friday, May 16, 2003  10:34 AM

I agree with this position

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

From: Randy Brukardt
Sent: Friday, May 16, 2003  11:54 AM

I second (fourth?) this opinion with one caveat.

"limited with" does not work with generics (since the model doesn't allow
looking in generic instantiations). Child units originally did not work with
generics, and we were eventually forced by user pressure to add the
(misguided in my view) generic child units. I fear that we're going to have
the same reaction to "limited with". However, doing that would destroy the
implementability of "limited with" for most compilers (because you'd have to
process withs, meaning the entire dependency mechanism would have to be
repeated). I personally can't think of how generics could be used in this
problem, but I'm certain someone else can and will.

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

From: Tucker Taft
Sent: Friday, May 16, 2003  2:09 PM

Here is an implementation model for the "incomplete type completed in
a child" proposal (ai-00217-07).

When parsing a type declaration, a child package prefix would
be permitted.  If the declaration turns out to not be for
an incomplete type, then a parse error would be indicated.

The incomplete type semantic analysis routines would take
the package prefix as an additional parameter.  If there is
a non-null package prefix, then an "incomplete" package would
be created, if not already existing, and the incomplete type
would be introduced into the incomplete package.

A list of all the incomplete packages in the current region would be
maintained, along with an indication of which, if any, of
the incomplete types came from incomplete_type_declarations
occurring in the visible part.  If there is at least one, the incomplete
package can only be "completed" by a public child or a nested package
declared in the visible part; otherwise it can be completed
by a private child or a nested package declared in the private part.

If the incomplete package is completed by a nested package, all of
the incomplete types are connected up to their completions; the completions
must exist in the visible part of the nested package and be visibly tagged
if so required; the nested package must be in the visible part if any of
the incomplete types came from incomplete_type_declarations in the
visible part.

Once completed, the incomplete package is removed from the list of incomplete
packages associated with the current region.  If there are any incomplete
packages left on the list when the end of the private part of
the current region is reached, then it is an error if the
current region is not that of a (non-generic) library package.
Presuming it is a non-generic library package, then this list is preserved
in the symbol table of the library package, and an appropriate set
of linker directives are created to indicate that the corresponding
child packages are "needed" in any partition that includes the
enclosing library package.

When a child package is compiled, a check in the symbol table of the
parent package is made to see if it corresponds to one of the
incomplete packages needing completion.  If so it is checked
that the child is a public child if so required, and that all
of the needed completions exist in the visible part, and are
visibly tagged if so required.

When a name is found to denote an incomplete package, a check
is made whether a library-unit renaming of the package has been
mentioned in an in-scope "with" clause.  If so, the same view as
that provided by the library-unit renaming is provided.  If not,
then only the incomplete types in the incomplete package are
visible.

When a name is found to denote a package renaming that is not
a library unit renaming, a check is made to see whether a corresponding
incomplete package is visible, and not "overridden" by a library
unit renaming.  If so, then the name provides a view of the
incomplete package.  Otherwise if the name denotes an enclosing
region, then the current view of this enclosing region is provided.
Otherwise, the name provides a view of the visible part, augmented with
any public children mentioned in in-scope with clauses.

When dereferencing an access type whose designated type is an
incomplete type, a check is made (as usual) to see whether the
full type is visible.  If the incomplete type is declared
in an incomplete package, then this involves seeing whether the
full package_declaration is visible, either directly or via a
library-unit renaming.  This is essentially the same check
performed when traversing a non-library-unit package renaming,
to determine whether the full view of the package should be
provided.

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

From: Pascal Leroy
Sent: Wednesday, May 21, 2003  9:52 AM

In implementation terms, the thing that bothers me more with the "type
C.T" model is the syntax change.  All the rest should be feasible
(although incomplete packages make me a bit nervous).

Coming back to the syntax: changing the parser is of course trivial.
However we depend on having a Diana representation that mimics the
language syntax.  So the Diana representation would need to have a
selected node as the as_id field of a type declaration.  Now there is a
gazillion places in our compiler where we land on a type declaration and
look at its as_id field.  All these places would become potential bugs
because once in a blue moon we would find a selected node instead of an
identifier, and we would query the wrong attributes.

We have had the same issue in Ada 95 with child units, and I am still
fixing bugs there.  References to types are as pervasive (or maybe more
pervasive) than references to units, so I expect the same stream of
bugs.  And this is an area of the language which will be less tested,
because while most Ada 95 programs make pretty extensive use of child
units, mutually-dependent types are only going to be used occasionally.

I would like to hear if other implementers have the same concern or if
this is somehow Rational-specific.

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

From: Ed Schonberg
Sent: Wednesday, May 21, 2003 10:51 AM

In GNAT I don't expect the syntax to cause much problems. Once the
declaration is processed, most references are to entities (symbol table
entries, if you prefer) rather than to the original tree itself. My
concerns are much more with issues of double visibility of incomplete types.
In our original implementation of with_type clauses, if a full view of the
type was in scope, the incomplete view was hidden and that was that. I
suspect that the current limited_with proposal (as well as the others) does
not permit such a simple model, but I have not examined the latest examples.

Having to keep two views of packages (one limited, one not) would be an
earthquake in the GNAT front-end, to be avoided at all costs.

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

From: Randy Brukardt
Sent: Wednesday, May 21, 2003  1:16 PM

I haven't looked at this carefully yet (but I will). But I don't think that
the syntax per-se is a major problem. My concern with it is that it will
have to be supported on all type declarations (otherwise the syntax is
ambiguous). Because of the way our compiler is structured, we'll open a new
scope for the phony package. The net effect is that all type definitions
will have to be prepared to close it, otherwise the error would cause a
never-ending cascade of other errors. Which is a number of places, but the
bugs would only occur in programs which used the feature, and which were in
fact illegal. So I hardly can claim that that is a critical problem.

I'm more concerned about dealing with the phony package needed, but that's
shared between child type stubs and limited with.

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

From: Pascal Leroy
Sent: Wednesday, May 21, 2003  3:07 PM

...
> Having to keep two views of packages (one limited, one not) would be an
> earthquake in the GNAT front-end, to be avoided at all costs.

We are in agreement here, and both Randy and I had that same concern.  I
believe that the latest write-up of all these AIs has the property that,
when processing a given compilation unit, you only have visibility on
one view of a given package, and which view you see is determined at the
end of the context clause.  In some cases this is achieved by the
limited view hiding the full view (that's to avoid ripple effects) but
at any rate the two never meet.

Doing otherwise would be an earthquake for us, too.

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

From: Ed Schonberg
Sent: Wednesday, May 21, 2003  3:45 PM

This is most reassuring! I will have a more detailed evaluation of the
difficulties that the remaining proposals might present for the GNAT
front-end, before the ARG meeting in Toulouse.

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

From: Tucker Taft
Sent: Wednesday, May 21, 2003 12:03 PM

I don't know how "religious" you are about
reflecting the syntax in the Diana, but it seems
you could have an optional package-prefix attribute,
rather than changing the ID to be a selected node.
That would seem to be less likely to cause bugs,
since you would only need to look at the package-prefix
when reconstructing the source, and when initially
deciding in which symbol table to place the incomplete
type.

But of course giving another implementor advice
about their compiler is usually pretty hopeless...

For what its worth, I would think the implementation of
"type C.T" would be closer to GNAT's "with type P.T"
implementation than would the implementation of
"limited with."  Essentially "withing" a package P that
contained a "type C.T" would be roughly equivalent
to doing the "with P;" followed by "with type P.C.T;"

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

From: Pascal Leroy
Sent: Wednesday, May 21, 2003  3:42 PM

Of course, that crossed my mind, but I think it would cause trouble in
some places.  For instance, when an error message includes a type name,
it would be good to be able to point at the selected node, rather than
screwing around trying to see if we are on a "type C.T" node.

This being said, I think we would have to "reify" the incomplete package
in the Diana for other reasons, so an option would be to point at the
type declared in the incomplete package.

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

From: Tucker Taft
Sent: Friday, May 16, 2003  12:14 PM

I did some more work on this to bring its handling of
non-library-unit renamings in conformance with the
direction established in the most recent "limited-with"
version.

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

You will find additional wording for 3.10.1(11)
and 8.5.3(4), and some additional discussion.

The basic idea is that non-library-unit
renamings don't provide more visibility than what you
would see via some other name that denotes the same
package.  On the other hand, by withing a library-unit
renaming of a package, you get visibility on the
full visible part (at least), and this in turn hides from all
visibility the incomplete types introduced by "type C.T"
(or limited with, in that proposal).

The net effect is that all views of a package are the
same, where non-library-unit renamings fall back
to the visibility of other views already visible,
while library-unit-renamings pull all views up to
at least be the full visible part (plus any public
children).

I think this matches what happens with the
most recent "limited with."  Basically it means
that the presence of a visible non-library-unit
renaming doesn't have any effect on the view of
a package that is provided via some other name.  This might
be considered eliminating another kind of
undesirable "ripple" effect due to adding or
removing a non-library-unit renaming in some random
package that happens to be visible.

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

From: Pascal Leroy
Sent: Monday, May 19, 2003  8:23 AM

Tuck's AI has:

>     incomplete_type_declaration ::=
>       TYPE [package_identifier.]defining_identifier[discriminant_part]
>         [is tagged];

I am wondering how this proposal handles the following case:

Say that you have a root library unit R, with two children C1 and C2,
and that C1 has a child GC1 and C2 a child GC2.  Imagine moreover that
all these packages declare types, and that you need to establish a
circularity between R.C1.GC1 and R.C2.GC2.  This is not an unlikely
situation.  Think of big class hierarchies where you have
cross-dependences between (sub)classes.  Claw comes to mind.

My first thought was that you would declare the incomplete types in R:

	type C1.GC1.T;
	type C2.GC2.T;

However this has two problems:

1 - It is forbidden by the syntax.
2 - The incomplete types have to be declared "too high" in the
hierarchy, in the sense that they are visible to R.C1 and R.C2 even
though they are not needed there.  This is a situation that we try to
avoid in Ada.

I can't think of a solution to this problem that doesn't force you to
screw up your unit hierarchy.  Maybe renamings can save the day, but it
would be singularly awkward to have to resort to renamings in this
simple case.

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

From: Tucker Taft
Sent: Monday, May 19, 2003  12:33 PM

I'm not sure I see the problem.  Doesn't the following work:

    package R.C1 is
       type GC1.T;
    end R.C1;

    package R.C2 is
       type GC2.T;
    end R.C2;

    with R.C2;
    package R.C1.GC1 is
       type T is ...
       type GC2_T_Ptr is access C2.GC2.T;  -- acc-to-incomplete
    end R.C1.GC1;

    with R.C1;
    package R.C2.GC2 is
       type T is ...
       type GC1_T_Ptr is access C1.GC1.T;  -- acc-to-incomplete
    end R.C2.GC2;

So long as the incomplete type and the full type can be declared in
different compilation units, I think you can break the circularity.

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

From: Pascal Leroy
Sent: Wednesday, May 21, 2003  9:38 AM

You're right, it works.  Somehow I was hoping for a solution based only
on parent/child visibility, without with clauses.  Apparently that's not
always possible.

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

From: Randy Brukardt
Sent: Tuesday, June 17, 2003  2:12 AM

Here is the implementation report I promised at the last meeting for all
three live AI-217
proposals. I know that these are really late, but at least you'll have three
days in which to read them. (Of course, if you're at Ada Europe already,
that probably is really about 3 minutes. :-(

---

Implementation of AI-217 alternatives in Janus/Ada.

This report looks at the implementation of AI-217 alternatives in Janus/Ada.
Since it is specific to this compiler, it uses a lot of internal
technology/terminology. The general conclusions should remain correct, however.

----

Type stubs, AI-217-05.

----

Type stubs would be implemented by taking advantage of the existing structure
of types. Incomplete types have a separate type record (and thus Type_Ptr
number - note to readers: Type_Ptrs are actually Integers in the "Fortran
pointer" style); they are linked to their completing type via the Full_Def
field. (This field is shared with private types, thus the name.) This field
defaults to Null_Type (0), meaning "no type". If a type lookup finds a
Full_Def of "no type", it stops at that point and returns the incomplete
type (thus giving only the properties allowed of incomplete types).

Fields would be added to provide the name of the completing type and
completing package; these would have to be written into the .SYM file.
Existing routines for handling identifier numbers and expanded names would
be used.

The units mentioned in limited with clauses would be added to a new, global
list of identifiers, during the processing of with clauses. (This is very
similar to what is done for other context clauses.)

When a stub declaration is processed, a check would be made that the package
named is in fact mentioned in a limited with (by consulting the limited with
list). An incomplete type record would be created, with the stub package
and type name filled in. Otherwise, this processing would be identical to
that of a normal incomplete type; probably, the code to declare such a type
will be shared.

The .JRL file format would be extended to include a list of limited withs.
This would be very similar to the existing list of regular withs (but without
the time stamps). This would be used to make the post-compilation checks.

As .SYM files are loaded during the processing of context clauses, a list will
be created pointing to all of the type stubs. Once all of the context clauses
have been processed (and thus, the complete symboltable [closure] is loaded,
including any indirectly withed units), this list will be walked. Any type
stubs whose completing type is in the symbol table because of a with clause
or a library level renaming will have the Full_Def filed altered to point
at the completing type. (In this case, most lookups will use the completing
type - the thus the full properties of the type - for all purposes, including
type matching). If the type is tagged, a similar operation would be done on the
class-wide incomplete (class-wide types are separately expressed in the
Janus/Ada type table).

Similarly, when a type declaration is processed, the list will be searched
for any type stubs for this type. If any are found, checks are made (that
the type has the proper visibility and properties), and the Full_Def field
of the incomplete type is set to point at the new type. (This will insure that
the type is treated as complete within the scope of the full type.) Class-wide
types for tagged types would also be handled. I am proposing using a simple
list search here, as I don't expect this to be long (only a few stubs in each
program) or a bottleneck. However, if profiling proves that it is a problem,
the list could be organized by package names so that only stubs pointing at
the current unit would be searched.

The linker front-end (used in each of the different Janus/Ada linkers/binders)
would be modified to implement the post-compilation checks. This mainly would
involve looking at each unit that is mentioned in a limited with clause,
insuring that it actually specifies a unit, and then looking in the with
list for that unit to insure that the first unit (the one with the limited
with clause) is withed.

I believe that we've dropped the extra dereference stuff (it's still in the
write-up), so I won't discuss implementing that.

----

Child Type stubs ("type C.T;"), AI-217-06.

----

These would be implemented very similarly to type stubs. However, the
symbol table changes would be much more extensive.

A new bit field ("ghost") would need to be added to packages to specify that it
is not a real package (and thus cannot be used in "Use" clauses). This is
mildly annoying, because we don't have any free bits in the current symbol
table recond (thus, making it bigger).

Similarly, a field would be added to the incomplete type record to
specify the package name of the child; these would have to be written into
the .SYM file. Existing routines for handling expanded names would be used.

Processing a stub declaration is complicated by the new syntax. Janus/Ada
is purely syntax-directed (in the sense of the Dragon book). The Janus/Ada
grammar has a "hook" at the begining of the processing of a type declaration
to create an empty symboltable record with the proper name. (Otherwise, that
happens too late; there is nowhere to write the names of any components of
the type, which are 'children' of the type name.) Because of the limitations
of LALR(1) grammars (and the fact that we consider incomplete types to be
"real" type_declarations), we would have to support the type C.T ... syntax
for all types.

If a type of the form of type C.T ... is seen, we would need to create a
symboltable record for the package C (with the "ghost" bit set) at the current
place in the symboltable, open a scope, and then create the dummy record for
type T and open its scope. Of course, it is critical that the extra open scope
be closed, so we would have to include the fact that this scope was opened on
the parse stack. This would require a new kind of parse stack record
[currently, we're just reusing the terminal stack item to pass the identifier
up].

Each production that closes a type definition would have to check this parse
stack item and pop the scope if needed. There are roughly 7 such places (I
didn't check this carefully).

Other than this annoyance, a child type stub would be processed almost the same
as a regular incomplete declaration. An incomplete type record would be
created, with the child package name filled in. Otherwise, this processing
would be identical to that of a normal incomplete type; probably, the code to
declare such a type will be shared.

As .SYM files are loaded during the processing of context clauses, a list will
be created pointing to all of the type stubs. Once all of the context clauses
have been processed (and thus, the complete symboltable [closure] is loaded,
including any indirectly withed units), this list will be walked. Any type
stubs whose completing type is in the symbol table because of a with clause
or a library level renaming will have the Full_Def filed altered to point
at the completing type. (In this case, most lookups will use the completing
type - the thus the full properties of the type - for all purposes, including
type matching). If the type is tagged, a similar operation would be done on the
class-wide incomplete (class-wide types are separately expressed in the
Janus/Ada type table).

The .JRL file format would be extended to include a list of children
required by child type stubs (I think we'll want a snappier name for this
concept!). This would be very similar to the existing list of regular withs
(but without the time stamps). This would be used to make the post-compilation
check that the child unit is included in the closure of the partition.

Similarly, when a type declaration is processed, the list will be searched
for any type stubs for this type. If any are found, checks are made (that
the type has the proper visibility and properties), and the Full_Def field
of the incomplete type is set to point at the new type. (This will insure that
the type is treated as complete within the scope of the full type.) Class-wide
types for tagged types would also be handled.

The linker front-end (used in each of the different Janus/Ada linkers/binders)
would be modified to implement the post-compilation check. This mainly would
involve looking at each unit that is listed as being required by a stub, and
insuring that it actually is included in the partition.

----

Limited with, AI-217-07.

----

This is a very different beast.

For each withable unit, we would define a new kind of symbol table file, the
.LYM file (limited with file). This file would have the virtually the same
format as the existing .SYM file (so we could use most of the existing
code to read it), but would contain only packages and incomplete types for
each type declared in the package.

Following an idea suggested by Pascal, the .LYM files would use a hash value
for obsoleteness checking. Thus, they can be recompiled as many times as
necessary with changing the hash (presuming that no changes were made).

An option would be added to the compiler to compile only an .LYM file. This
option would cause the compiler driver to run a special second pass of the
compiler after running the standard parsing pass. This special second
pass would do very little other than create a skeleton symbol table and
write it out as an .LYM file.

A normal compilation of a unit would write the normal .SYM file, but also
would write (or rewrite) the .LYM file. That insures that the .LYM file is
in-sync with the object code; if the source code was changed after the .LYM
was created, the compilation of that source must change the .LYM in order to
cause a linker error. (We don't want to allow the stubbed type to be removed
or changed from tagged to untagged without it being detected.)

Normal compilation would always create an incomplete type record for each
type declared in a spec. These records would always be completed (by setting
Full_Def) in the .SYM file. But the records would be left incomplete (and
would be the only type records in use) for the .LYM file.

The .JRL format would be changed to include the .LYM hash for each unit, and
the .LYM hash for 'self'. As with the .SYM timestamps, the .LYM hash would need
to be identical for each occurrence of a unit. This check would be done in
parallel with the existing .SYM timestamp checks in the linker front-end.

If a limited with clause mentions a unit (and the unit is not otherwise loaded
in the closure), the .LYM file would be loaded. This would provide a skelton
of the package. Note that the .SYM includes all of the items declared in the
.LYM; that includes the incomplete stub types. That means that if the closure
includes a package which only had visibility on the .LYM, normal .SYM file
loading type lookup will match those incomplete types to the equivalent ones
(which are completed) in the full .SYM. Thus, we do not need to do anything
special to load these types.

This implementation will run into significant problems if we need to have
limited views when the full view is available in the symboltable. The rules
as contemplated appear to have this effect. Since Janus/Ada always loads the
full closure, the full view is naturally available even when the package
itself is not visible. Of course, Janus/Ada already has to stand on its head
to hide the names (a sigificant source of bugs when code forgets to check
whether a name is visible before using it). However, types themselves do not
have visibility -- they are global constructs kept in a separate table. Thus,
any time the full view is in the closure, the types would appear completed.
Fixing this looks expensive -- either by adding visibility code to type lookup
(that is, using visibility information to decide whether to follow the
Full_Def field), or by somehow breaking the Full_Def field (by setting it to
Null_Type). The problem with both of these approaches is that any code
generated for the type will expect (since the full view is in the closure)
that the full type is available. So substantial head-standing will be needed
(presuming that code can be generated for these types; based on previous
experience, I'd say that it is likely that it can, although I can't think of
an example at 2 am).

Another problem with this implementation is that type numbers are defined
relative to the package that contains them. The loading algorithm assumes that
the numbers for any particular package are contiguous. That won't be true of a
.LYM package created from a full compile. In such a compile, the incomplete
type records will be scattered about, with the various full definitions and
subtypes between them. That is something that could not be changed, because the
full compilation could not know what types and packages are yet to come in the
compilation. We're clearly not going to be including the the full definitions
and subtypes in the .LYM file; but I don't know if eliminating this invariant
will cause major problems. [This was the last thing I thought of while doing
this report, and it is too late to try to figure it out. Sorry.] An
unappealing alternative would be to force the (separate) creation of a limited
view first, load that into the symboltable, then compile the full view.
However, this would require lots of extra work to eliminate the errors that
would naturally occur when declaring a type or package that is already in the
symboltable.

Finally, a substantial reworking of the COrder compilation order tool will be
needed. While the type stub proposal will need only very minor tweaking here
(just to allow and ignore the limited with clause), and the child type stub
proposal will need no tweaking at all, this would require a substantial
rewrite.

Pascal has suggested that the easiest way to handle this would be to simply
generate .LYMs (limited views) for all specs in the program (in any order,
the order cannot matter). However, processing a single file will take 1-2
seconds (mostly because of the overhead of loading three programs and three
data files). If you have a real program with 150 specs (such as the Janus/Ada
compiler itself), that means that generating all of the .LYMs each time would
take as much as an extra 300 seconds. This is way too long (think of adding
that in front of every compile, especially after a user has made a one line
change to some body). So it's clear that that approach will not work.

Thus, we'll have to include a timestamp in the .LYM, and be prepared to compare
it the timestamp of the source code. Then, any .LYMs out of date (or missing),
will have to be regenerated. Note that this is a pure greater than check; the
.LYM may be much newer than the source code, as it could be regenerated by
later full compiles forced by normal obsoleteness.

Doing so separately (as in Pascal's original suggestion, just limited to the
units that have changed since the last compilation) would be the quick and
dirty solution. But even that is likely to be too slow in some instances.
However, a full solution (which would fully integrate the processing of
limited withs into the compilation ordering decisions) is likely to be very
expensive, especially as the code in question is rather "magic" as it is. So,
we're probably stuck with the "limited compile everything that might have
changed" approach.


----

Conclusions:

Child type stubs and regular type stubs have similar implementation costs in
Janus/Ada. However, child type stubs are more likely to cause problems, as they
require changes in quite a bit more existing code (to handle making the use
of "ghost" packages illegal in most contexts, and to handle the declaration
[and "undeclaration"] of the "ghost" package in the first place.

Limited with is quite a bit more expensive, both because a new, separate
compiler pass is needed (with the attendant maintenance problems of having
"separate but parallel" code), and because an entire new kind of symbol table
file has to be devised and loaded. By keeping the formats similar to existing
code, most of the existing code can be reused. Still, a number of problems
with the straightforward solutions have been noted above (the type number
invariants, and the visibility of complete types), which are likely to make
this even more work, and may require reworking of parts of the compiler
that don't seem to be "naturally" involved.

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


Questions? Ask the ACAA Technical Agent