Version 1.2 of ais/ai-00271.txt

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

!standard 07.01 (02)          01-06-13 AI95-00271/01
!class amendment 01-05-31
!status work item 01-06-02
!status received 01-05-31
!priority Medium
!difficulty Hard
!subject Handling mutually recursive types via package "abstracts"
!summary
A new construct, called a package "abstract," is proposed as a potential solution to the "mutually recursive types across packages" problem. This is an alternative to the "with type" proposal of AI-217.
A package "abstract" is an extract of a package specification, containing types, and potentially subpackages, representation items, and static constants that may be needed to allow two packages to declare mutually recursive types. A package abstract is referenced by an "abstract with" clause. A package specification must conform to its package abstract, if any.
!problem
Ada only allows mutually recursive types to be declared 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.
!proposal
Three new constructs are proposed: a package_abstract, a with_abstract clause, and an incomplete tagged type.
PACKAGE ABSTRACT:
A package_abstract has the following syntax:
package_abstract ::= library_package_abstract | nested_package_abstract
library_package_abstract ::= abstract {abstract_item}
nested_package_abstract ::= abstract {abstract_item} package defining_identifier ;
The syntax for library_unit_declaration is amended as follows:
library_unit_declaration ::= subprogram_declaration | [library_package_abstract] package_declaration | generic_declaration | generic_instantiation
In other words, a package_abstract is only allowed before a library unit declaration for a non-generic package. The reason for this restriction is as follows:
- Subprogram_declarations do not export types or constants.
- Generic_declarations and generic_instantiations cannot participate in
circular dependencies.
- The abstracts for nested packages are given as nested abstracts in the
top-level abstract. That's because we want abstracts to be entered "early" in the environment. If nested packages had abstracts, they would have to be entered "early" too. But then they could depend on declarations appearing earlier in the same library unit declaration, so this library unit declaration would have to be partially processed by the compiler, and we don't know how to specify this in a way that works for both source-based and library-based compilers.
A package abstract may contain only the following kinds of declarative items:
abstract_item ::= incomplete_type_declaration | abstract_interface_declaration -- see AI-251 on interface types | type defining_identifier is access_type_definition ; | defining_identifier_list : constant [subtype_indication] := static_expression ; | attribute_definition_clause | nested_package_abstract
Representation pragmas are also permitted.
No pool-specific access types may be declared in a package abstract.
All expressions appearing in a package_abstract must be static.
The specification of a package must "conform" to its abstract, if any, in the following sense:
- Each incomplete type declaration must be completed in the visible
part of the specification of the package by a full_type_declaration, a private_type_declaration, or a private_extension_declaration.
- For each nested_package_abstract, there must be a
package_specification with the same defining_identifier in the visible part of the specification of the package. This package must in turn conform to its abstract. (This wording is probably not quite right, but the purpose should be clear.)
If an access type is declared in a package abstract, all aspects of its representation except for its storage pool are frozen at the end of the package abstract. As indicated above, the access type must not be pool-specific.
The only representation items permitted in the package specification for the access type are those that apply to the storage pool of the type (Storage_Pool, Storage_Size).
The elaboration of a package abstract has no effect.
The actual "collection" for the access type is not created until the package specification is elaborated. An allocator is illegal for an access type declared in an abstract, though not for the corresponding access type declared in the specification. Similarly, an access type in an abstract must not be passed as an actual for a formal access type in a generic instantiation.
WITH ABSTRACT CLAUSE:
with_clause ::= with_abstract_clause | with_unit_clause
with_unit_clause ::= with library_unit_name {, library_unit_name}
with_abstract_clause ::= with abstract library_unit_package_name {, library_unit_package_name};
[Anyone has a better name for with_unit_clause?]
The scope of a with_abstract_clause that appears on a library_unit_declaration which is a package_declaration with a library_package_abstract consists only of the declarative region of the package_declaration. The declarative region of the library_package_abstract is not included. [Otherwise we are back to a circularity where two packages that have abstracts cannot depend on each other.]
The scope of a with_unit_clause that appears on a library_unit_declaration which is a package_declaration with a library_package_abstract consists of the declarative region of the library_package_abstract (which in turns include the package_declaration, etc.).
A with_abstract_clause on a compilation unit makes the abstract (and only the abstract) of each specified package visible within the compilation unit. It has no effect if the specification of the package is visible due to a with clause on some ancestor (or corresponding declaration) of the compilation unit.
A with_unit_clause on a compilation unit makes both the abstract and the visible part of each specified package visible within the compilation unit.
Within the abstract for a child package, the abstract of its ancestors, if any, is directly visible.
If a package specified by a with_abstract_clause on some compilation unit is a child package, then the abstract of its parent and each further ancestor is made visible as if it were denoted by an explicit with_abstract_clause.
If an abstract or specification of a package is made visible by an abstract with clause, then a semantic dependence is created on that part of the package. [This in turn implies an elaboration dependence -- see 10.2(9).] The specification of a package has a semantic dependence on its abstract, if any.
The compilation model for abstracts is as follows:
- Adding a package_abstract to the environment causes the compiler to
process only the library_package_abstract part of the unit.
- Adding a library_unit_declaration which includes a package_declaration
to the environment causes the compiler to process both the library_package_abstract and the package_declaration.
- When a library_unit_declaration is compiled, and the environment
already contains a package_abstract for the same unit, and that abstract doesn't conform to the abstract of the unit being compiled, the compiler may remove the existing package_abstract from the environment. It is _not_ allowed to do this if the library_unit_declaration has a package_abstract which conforms to the one already entered in the environment.
- Post-compilation rule: it shall not be possible to run a partition
that contains a package_abstract and a package_specification which do not conform to each other.
INCOMPLETE TAGGED TYPE:
incomplete_type_declaration ::= type defining_identifier [discrminant_part] [is tagged];
An incomplete tagged type may be used as the type of a formal parameter prior to its completion, in addition to the normal places where an incomplete type may be used. Also, the Class attribute is defined for an incomplete tagged type. The class-wide type denoted by the Class attribute of an incomplete type may also be used as the type of a formal parameter. The Class attribute of a non-tagged incomplete type would be considered obsolescent.
Note that we are not proposing that an incomplete tagged type may be used as a return type, because of the many special rules and implementation issues associated with returning tagged types (e.g. functions becoming abstract, accessibility checks on returning limited by-reference types, dispatching on result coupled with tag indeterminacy, finalization associated with returning potentially controlled types, etc.)
!discussion
The concept of a package abstract has been discussed before, under different guises. At the time, the "with type" seemed somewhat more attractive. However, various technical difficulties associated with making access types visible via "with type" have reduced the capability of the "with type" proposal. Also, implementation discussions about how to implement "with type" have revealed some significant issues associated with whether or not the specification must be in the environment prior to compiling a "with type" clause. Other problems that have arisen relate to the need to implicitly create an "abstract" for a package to implement "with type," with the attendant issues associated with implicit structures in the program library, their longevity, their consistency, etc. Finally, "with type" requires creating some rather strange "fictions" to explain to the user what is really going on.
There seem to be two general ways this kind of problem is solved in other languages. One approach is to permit unrestricted forward references. This is the approach adopted by Eiffel, Java, and Smalltalk. The other approach is to require "forward" or "incomplete" declarations, sometimes called "signatures" or "partial revelations." This is generally the approach adopted by Ada, as well as Pascal, Modula, ML, C/C++, etc. The notion of a package "abstract" is analogous to the "incomplete" type in Ada, the signature in ML, and the partial revelation in Modula.
The "with type" approach seems to be a bit of an awkward half-way position, allowing a kind of "restricted" forward reference, lacking the generality of unrestricted forward reference, and lacking the flexibility of a true "forward" declaration, where representation items and other capabilities can be accommodated.
The package abstract approach seems more natural for Ada, and gives the user something to look at and conceptually "hold onto," which should make the concept easier to understand.
We considered separating the abstract and making it a new kind of library unit. However, this would have a significant impact on the tools which deal with the enviroment and the library units, in addition to the impact on the compiler proper. By putting the abstract in the same library unit as the specification, we are avoiding the extra burden of a new kind of unit. However, we need to define a two-phase compilation process, where the abstract is entered in the environment on a reference through a with_abstract_clause, and the specification is entered in the environment on a reference through a normal with clause.
From an implementation point of view, the "optionality" of a package abstract is somewhat analogous to the "optionality" of a subprogram specification. The two-phase compilation process is somewhat analogous to the two-phase process that has to take place to support mutual inlining (an optional capability).
!example
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.
with abstract Departments; abstract
type Employee; type Emp_Ptr is access all Employee;
package Employees is type Employee is private; 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 Assigned_To : Departments.Dept_Ptr; end record; end Employees;
with abstract Employees; abstract type Department; type Dept_Ptr is access all Department; package Departments is type Department is private; procedure Choose_Manager(D : access Department; Manager : access Employees.Employee); ... private type Department is record Manager : Employees.Emp_Ptr; end record; end Departments;
!ACATS test
!appendix

From: Tucker Taft
Sent: Saturday, June 02, 2001 1:03 PM

Here is one of my first homework assignments.
-Tuck
------------------
!standard 07.01   (02)                                01-05-31  AI95-002xx/00
!class amendment 01-05-31
!status received 01-05-31
!priority Med
!difficulty Hard
!subject Handling mutually recursive types via package "abstracts"

!summary

A new construct, called a package "abstract," is proposed as a potential
solution to the "mutually recursive types across packages" problem.
This is an alternative to the "with type" proposal.

A package "abstract" is an extract of a package specification, containing
types, and potentially subpackages, representation items, and static
constants that may be needed to allow two packages to declare mutually
recursive types.  A package abstract is referenced by an "abstract with"
clause.  A package specification must conform to its package abstract,
if any.  The elaboration of a package abstract has no effect.

!problem

Ada only allows mutually recursive types to be declared 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.

!proposal

Three new constructs are proposed: a package "abstract," an "abstract" with
clause, and an incomplete tagged type.

PACKAGE ABSTRACT:

    package_abstract ::=
        PACKAGE ABSTRACT defining_program_unit_name IS
            {package_abstract_item}
        END [[parent_unit_name.]identifier];


A package abstract may contain only the following kinds of declarative items:

   package_abstract_item ::=
       incomplete_type_declaration
     | abstract_interface_declaration    -- see separate AI on interface types
     | TYPE defining_identifier IS access_type_definition ;
     | defining_identifier_list : CONSTANT [subtype_indication]
         := static_expression ;
     | attribute_definition_clause
     | package_abstract

Representation pragmas are also permitted.

No pool-specific access types may be declared in a package abstract.

The specification of a package must "conform" to the abstract of the
package, if any, in the following sense:

   - Each incomplete type declaration in the abstract of the package
     must be completed in the visible part of the specification of
     package.  A conforming incomplete type declaration may also appear
     prior to this completion in the visible part of the specification.

   - Each access type declaration in the abstract must have a conforming
     access type declaration in the visible part of the specification.

   - Each constant or named number declaration in the abstract must
     have a conforming constant or named number declaration in the
     visible part of the specification.

   - Each representation item in the abstract must have a conforming
     representation item in the specification.

If an access type is declared in a package abstract, all aspects of
its representation except for its storage pool are frozen at the end
of the package abstract.  As indicated above, the access type
must not be pool-specific.

The only representation items permitted in the package specification
for the access type are those that conform to representation items
appearing in the abstract, and those that apply to the storage
pool of the type (Storage_Pool, Storage_Size).

The actual "collection" for the access type is not created until
the package specification is elaborated.  An allocator is illegal for an
access type declared in an abstract, though not for the corresponding
access type declared in the specification.  Similarly, an access
type in an abstract must not be passed as an actual for a formal
access type in a generic instantiation.


ABSTRACT WITH CLAUSE:

   abstract_with_clause ::=
        ABSTRACT WITH library_unit_package_name {, library_unit_package_name};

An abstract with clause on a compilation unit makes the abstract of
each specified package visible within the compilation unit, and hides
the specification of the package even if it is visible due to a with
clause on some ancestor (or corresponding declaration) compilation unit.
Similarly, a (normal) with clause on a compilation unit
makes the specification visible, and hides the abstract even
if some ancestor (or corresponding declaration) compilation unit made it
visible.  A given compilation unit must not mention a package in a (normal)
with clause and specify the same package in an abstract with clause.
[NOTE: This is to ensure that order of with clauses does not matter.]

Within the abstract for a child package, if the abstract of
its parent is not already visible due to an abstract with clause,
then the visible part of the specification of the parent is visible.
Hence the abstract of the child has direct visibility on only the declarations
of the abstract of the parent if the abstract of the parent is visible.
If the abstract of the parent is visible, then either the specification
or abstract of further ancestors is visible corresponding to which
is visible in the abstract of the parent.

If a package specified by an abstract with clause on some compilation unit
is a child package, then either the specification
or abstract of its parent and each further ancestor is made visible
within the compilation unit, as determined by which is visible within
the specified child.

If an abstract or specification of a package is made visible by
an abstract with clause, then a semantic dependence
is created on that part of the package.  [This in turn implies
an elaboration dependence -- see 10.2(9).]
The specification of a package has a semantic dependence on its
abstract, if any.

NOTE: At no point should both the specification and the abstract of a package
be visible by the "same" name.  However, (a rename of) the specification
may be visible under one name, while (a rename of?) the abstract is visible
under a different name.

[TBD: Should "package abstract P renames Q;" be supported?  Seems OK.]
[TBD: Should a package abstract be permitted as a local package?
   I don't see why not...]
[TBD: Should package renames be permitted inside of a package abstract?
   Seems unnecessary.]

INCOMPLETE TAGGED TYPE:

   incomplete_type_declaration ::=
       TYPE defining_identifier [discrminant_part] [IS TAGGED];

An incomplete tagged type may be used as the type of a formal parameter
prior to its completion, in addition to the normal places where
an incomplete type may be used.  Also, the Class attribute is defined for
an incomplete tagged type.  The class-wide type denoted by the Class
attribute of an incomplete type may also be used as the type of a
formal parameter.  The Class attribute of a non-tagged incomplete
type would be considered obsolescent.

Note that we are not proposing that an incomplete tagged type may be
used as a return type, because of the many special rules and implementation
issues associated with returning tagged types (e.g. functions becoming
abstract, accessibility checks on returning limited by-reference types,
dispatching on result coupled with tag indeterminacy, finalization associated
with returning potentially controlled types, etc.)
may also be used as the type of a formal parameter.

!discussion

The concept of a package abstract has been discussed before, under
different guises.  At the time, the "with type" seemed somewhat more
attractive.  However, various technical difficulties associated with
making access types visible via "with type" have reduced the
capability of the "with type" proposal.  Also, implementation discussions
about how to implement "with type" have revealed some significant issues
associated with wheter or not the specification must be in the
environment prior to compiling a "with type" clause.  Other problems
that have arisen relate to the need to implicitly create an "abstract"
for a package to implement "with type," with the attendant issues associated
with implicit structures in the program library, their longevity, their
consistency, etc.  Finally, "with type" requires creating some rather
strange "fictions" to explain to the user what is really going on.

There seem to be two general ways this kind of problem is solved in
other languages.  One approach is to permit unrestricted forward
references.  This is the approach adopted by Eiffel, Java, and Smalltalk.
The other approach is to require "forward" or "incomplete" declarations,
sometimes called "signatures" or "partial revelations."  This is generally
the approach adopted by Ada, as well as Pascal, Modula, ML, C/C++, etc.
The notion of a package "abstract" is analogous to the "incomplete"
type in Ada, the signature in ML, and the partial revelation in Modula.

The "with type" approach seems to be a bit of an awkward half-way
position, allowing a kind of "restricted" forward reference, lacking
the generality of unrestricted forward reference, and lacking the
flexibility of a true "forward" declaration, where representation
items and other capabilities can be accommodated.

The package abstract approach seems more natural for Ada, and gives
the user something to look at and conceptually "hold onto," which
should make the concept easier to understand.

From an implementation point of view, the "optionality" of a package
abstract is analogous to the "optionality" of a subprogram specification,
and presumably similar program library mechanisms can be used to
accommodate them.

!example

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.

    package abstract Employees is
        type Employee;
        type Emp_Ptr is access all Employee;
    end Employees;

    package abstract Departments is
        type Department;
        type Dept_Ptr is access all Department;
    end Departments;

    abstract with Departments;
    package Employees is
        type Employee is private;
        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;
    end Employees;

    abstract with Employees;
    package Departments is
        type Department is private;
        type Dept_Ptr is access all Department;
        procedure Choose_Manager(D : access Department;
          Manager : access Employees.Employee);
        ...
    end Departments;

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

From: Randy Brukardt
Sent: Saturday, June 2, 2001 11:36 PM

I've cleaned up a couple of editing errors, and posted this as AI-271.

I didn't see any glaring problems reading it over, but I haven't thought
about implementation yet.

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

From: Pascal Leroy
Sent: Tuesday, June 5, 2001 8:44 AM

> Here is one of my first homework assignments.

I am really amazed, I mean, only two weeks after the meeting ;-)

A number of questions/comments that come to mind on reading your proposal (in
no particular order):

0 - Even though the new proposal has a number of advantages over the 'with
type' one, I must say that I am quite uncomfortable with the notion of adding
a new kind of library unit.  Probably the impact on compilers will be about
the same for both proposals, but all the little peripheral tools that deal
with the library will be impacted by package abstracts.  In Rational Apex we
would have to add tools to navigate to and from an abstract, to create an
abstract from the specification (or vice-versa), to locate the usages of
abstracts, etc.  That's a significant effort, which comes in addition to the
compiler work.  I am not saying that this kill the proposal, but it must be
taken into account when evaluating the costs and benefits.

1 - Am I right in assuming that a package abstract may 'with' any unit?  This
seems a bit peculiar because the abstract is not elaborated, and the 'withed'
unit is, but that's probably OK.  An alternative model would be to say that
abstracts may only 'with' other abstracts and pure units, but that would
restrict the possible usages.

2 - In the 'with type' proposal, we disallowed discriminants in the incomplete
type.  Do we want to disallow discriminants in the incomplete_type_declarations
that are package_abstract_items?  On the one hand, allowing discriminants could
open a can of worms (although I don't see why it would be worse that
incomplete-types-completed-in-the-body).  On the other hand, disallowing them
makes it impossible to have discriminated types that participate in a circular
dependency, because the full and incomplete declarations have to conform.

3 - We should disallow constraints on access types declared in package
abstracts, if only because such constraints would probably require some form of
elaboration.

4 - For the same reason, address clauses should be disallowed for constants
declared in package abstracts.

5 - I don't like the notion that the visibility of an abstract child (whether
it sees the abstract or the specification of its parent) depends on the
presence of an 'abstract with' for the parent.  The notion that adding/removing
a with clause for your parent has an effect seems distasteful to me.  What
would be wrong with saying that an abstract child always has visibility over
the specification of its parent?

6 - Since we have another AI about 'private with', should we add an 'abstract
private with' too?  (Yuck!)

7 - I don't think we want package abstracts for local packages.  The sole
reason of adding abstracts to the language is to break compilation
dependencies, so I don't see what would be the benefits of local abstracts.
This would only confuse the poor users.

That's all for today...

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

From: Robert Dewar
Sent: Tuesday, June 5, 2001 12:03 PM

One comment I have is that GNAT is in any case committed long term to its
impelmentation of "with type" since we have a significant user community
depending on this. Of course it might make sense to have two separate mechanisms,
but the argument would have to be strong.

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

From: Pascal Leroy
Sent: Tuesday, June 5, 2001 3:59 PM

I understand, and it is an issue which was raised during the meeting, but to
no avail since there was no ACT representative to give us his point of view.
It seems to me that, because the 'with type' proposed by the ARG in AI 217
is substantially different from what GNAT implements, you would have to
maintain two mechanisms anyway.  Of course, you might also decide not to
implement the mechanism designed by the ARG...

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

From: Randy Brukardt
Sent: Tuesday, June 5, 2001  4:35 PM

That might be the strongest argument in favor of a different mechanism, because
I think it is unlikely that any "with type" that comes out of the ARG is going
to be much like what GNAT has implemented:
   -- Because the GNAT implementation rules only work on a "source-based"
      implementation;
   -- Because the GNAT implementation only works if there is only one
      representation for access types.

The latter problem has meant that "with access type" was dropped completely
from the ARG proposal, and was replaced by an
implicit-conversion-rule-to-be-defined later.

So I think it is imperative that we look at the big picture here. Any "with
type" proposal from the ARG is necessarily going to also need an "implicit
access conversion" proposal to make it usable. One of the big benefits of
Tucker's current proposal is to make it possible to again include access types
in the items that can be imported. This means that "implicit access
conversions" can be round filed (John would say "binned", I believe) where it
belongs (IMHO). That greatly simplifies the total change.

Robert, you missed the discussion of "named anonymous access types" and their
associated run-time accessibility checks that Tucker is sure can be
implemented, but Steve Baird and I are doubtful about. Being able to forget
that would be worth a lot in my opinion.

Anyway, we still have to expose the gotcha's in Tucker's new proposal (Pascal
found some), but in the absence of major ones, I guess that I would prefer a
lot of pain to implement a feature that is known to be critical (as GNAT's
implementation of "with type" proves the need) rather than somewhat less pain
distributed over a bunch of features of dubious need [except that they are
needed to make the critical feature usable]. I doubt anyone will argue the
need for mutually dependent types, no matter how expensive. But I think a lot
of people will argue the need and even the desirability of more implicit
conversions between types. We would be in significant danger of ending up with
an "easy" implementation of a critical feature that is hard to use (because of
all of the access conversions that would be needed).

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

From: Tucker Taft
Sent: Tuesday, June 5, 2001  3:01 PM

Pascal Leroy wrote:

> 0 - Even though the new proposal has a number of advantages over the
> 'with type' one, I must say that I am quite uncomfortable with the notion
> of adding a new kind of library unit.  Probably the impact on compilers will
> be about the same for both proposals, but all the little peripheral tools
> that deal with the library will be impacted by package abstracts.  In
> Rational Apex we would have to add tools to navigate to and from an abstract,
> to create an abstract from the specification (or vice-versa), to locate the
> usages of abstracts, etc.  That's a significant effort, which comes in
> addition to the compiler work.  I am not saying that this kill the proposal,
> but it must be taken into account when evaluating the costs and benefits.

I also have been worrying about that...

I'm wondering whether we might consider just making it part of the spec rather
than a separate compilation unit, much like a private part.  An "abstract with"
would just make the "abstract part" of the specification visible. In this case,
we could make the relevant representation items associated with a type in the
"abstract" part automatically included, rather than having to physically move
them into the abstract part.  We would have to require that all expressions in
the representation item be static.

Here is a possible example:

    abstract with Employee;
    package Department is
      abstract
        type Dept;
        type Dept_Ptr is access all Dept;
      end abstract;

        type Dept is private;
        ...
    end Department;

    abstract with Department;
    package Employee is
      abstract
        type Emp;
        type Emp_Ptr is access all Emp;
      end abstract;
        type Emp is private;
        ...
    end Employee;

Obviously some work is needed on the syntax ;-).



> 1 - Am I right in assuming that a package abstract may 'with' any unit?  This
> seems a bit peculiar because the abstract is not elaborated, and the 'withed'
> unit is, but that's probably OK.  An alternative model would be to say that
> abstracts may only 'with' other abstracts and pure units, but that would
> restrict the possible usages.

Actually, an "abstract" is elaborated, but the elaboration
doesn't have any effect.  So I don't see a problem fitting
it into the elaboration order like any other unit.  It mostly
acts like a "pass through" in that it doesn't contribute
anything to elaboration, but it can pass through dependences.

>
> 2 - In the 'with type' proposal, we disallowed discriminants in the incomplete
> type.  Do we want to disallow discriminants in the incomplete_type_declarations
> that are package_abstract_items?  On the one hand, allowing discriminants could
> open a can of worms (although I don't see why it would be worse that
> incomplete-types-completed-in-the-body).  On the other hand, disallowing them
> makes it impossible to have discriminated types that participate in a circular
> dependency, because the full and incomplete declarations have to conform.

I think you are on "Ada 83"-think here.  You don't need to
put discriminants on an incomplete type in Ada 95, even if
the full type has them.  It seems unnecessary to permit
discriminants in an abstract, though I'm not sure I see
the problem either way.  I suppose the problem with having
discriminants is that you need that many more types visible.

> 3 - We should disallow constraints on access types declared in package
> abstracts, if only because such constraints would probably require some form of
> elaboration.

Or require that all expressions in an abstract be static
expressions (which I think we need to do).

>
> 4 - For the same reason, address clauses should be disallowed for constants
> declared in package abstracts.

Or just restrict expressions to being static, which would
probably accomplish much the same thing.

> 5 - I don't like the notion that the visibility of an abstract child (whether it
> sees the abstract or the specification of its parent) depends on the presence of
> an 'abstract with' for the parent.  The notion that adding/removing a with
> clause for your parent has an effect seems distasteful to me.  What would be
> wrong with saying that an abstract child always has visibility over the
> specification of its parent?

This prevents a parent and a child having a mutually
recursive type, which seems potentially quite useful.
>
> 6 - Since we have another AI about 'private with', should we add an 'abstract
> private with' too?  (Yuck!)

Yes, we need to integrate the "private with" with the
"abstract with" proposal somehow.  It may be that
we should use "abstract" as an modifier on the
package name rather than the "with" as a whole.
I went back and forth on that, but the interaction
with "private with" tends to push toward moving the
"abstract" after the with, and have it apply to
the immediately following package name only.  E.g.

   private with P, abstract Q, R;
would be equivalent to:
   private with P;
   private with abstract Q;
   private with R;

I suppose we could even move it to after the package name:

   private with P, Q abstract, R;


> 7 - I don't think we want package abstracts for local packages.  The sole reason
> of adding abstracts to the language is to break compilation dependencies, so I
> don't see what would be the benefits of local abstracts.  This would only
> confuse the poor users.

I don't see the problem with having local abstracts.  The
problem seems to exist for local packages just as much as
for library packages.  Of course, if we make abstracts part of
the specification, then we lose the ability to create
local mutual recursion...

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

From: Randy Brukardt
Sent: Tuesday, June 5, 2001  4:56 PM

> 0 - Even though the new proposal has a number of advantages over the
> 'with type' one, I must say that I am quite uncomfortable with the notion
> of adding a new kind of library unit.  Probably the impact on compilers will
> be about the same for both proposals, but all the little peripheral tools
> that deal with the library will be impacted by package abstracts.  In
> Rational Apex we would have to add tools to navigate to and from an abstract,
> to create an abstract from the specification (or vice-versa), to locate the
> usages of abstracts, etc.  That's a significant effort, which comes in
> addition to the compiler work.  I am not saying that this kill the proposal,
> but it must be taken into account when evaluating the costs and benefits.

This worries me too, but the "big picture" costs of "implicit access
conversions" also bother me. I'm willing to wait and see.

> 3 - We should disallow constraints on access types declared in package
> abstracts, if only because such constraints would probably require some form
> of elaboration.

We should disallow constraints on access types wherever possible :-) because
they cause all kinds of trouble, and are virtually worthless. No reason needed
(but it probably doesn't hurt to find one).

> 5 - I don't like the notion that the visibility of an abstract child (whether
> it sees the abstract or the specification of its parent) depends on the
> presence of an 'abstract with' for the parent. The notion that
> adding/removing a with clause for your parent has an effect seems distasteful
> to me.

I agree.

> What would be wrong with saying that an abstract child always has
> visibility over the specification of its parent?

I would have said the opposite: it has visibility only over the abstract of the
parent. I don't think that you usually need (or want) visibility on concrete
things in an abstract. The problem with that, is what happens if the parent has
no abstract? I'd say the answer is that the child sees nothing in the parent at
all. Duplicate definitions would get caught when the spec is compiled, so that
is no problem.

> 6 - Since we have another AI about 'private with', should we add an 'abstract
> private with' too?  (Yuck!)

Yes, of course. Indeed, I would expect that to be the most common use, as you
generally need those pointers at other objects in the full type declaration of
the new object. (Why the yuck??)

A more interesting question is whether we can have private abstract packages.
If we can, can the privateness of the abstract and the specification be
different? If we can't have private abstract packages, why not??

> 7 - I don't think we want package abstracts for local packages.  The sole
> reason of adding abstracts to the language is to break compilation
> dependencies, so I don't see what would be the benefits of local abstracts.
> This would only confuse the poor users.

Consistency, of course. What are the benefits of local packages? Not a heck of
a lot, but people find uses for them from time-to-time anyway. I'd expect any
rule restricting these to library units to feel "unnatural", and some users
will complain.

An obvious problem with both "with type" and "package abstract" is that they
don't work when all of the units are in the same compilation unit. That means
that you can't just take a bunch of compilation units and wrap them in a
wrapper package. I don't know if this is a real problem, but we should at
least think about it a bit.

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

From: Randy Brukardt
Sent: Tuesday, June 5, 2001  6:02 PM

My reply to Pascal crossed in the E-Mail with Tuck's (I want my DSL back!!!),
so here's a comment to Tuck:

> > 5 - I don't like the notion that the visibility of an abstract child
> > (whether it sees the abstract or the specification of its parent)
> > depends on the presence of an 'abstract with' for the parent.  The notion
> > that adding/removing a with clause for your parent has an effect seems
> > distasteful to me.  What would be wrong with saying that an abstract
> > child always has visibility over the specification of its parent?
>
> This prevents a parent and a child having a mutually
> recursive type, which seems potentially quite useful.

This makes sense to me. But what I don't understand is why an abstract child
would ever need access to the "regular" parent specification. And, if it does,
why would the "default" be to have complete visibility? I would expect it to be
the other way.

Of course, if the "abstract part" idea is adopted (with better syntax!), this
all becomes moot: the visibility is always on the parent full spec, and the
child cannot be mutually recursive with the parent (because that would make the
parent dependent on its own full definition).

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

From: Pascal Leroy
Sent: Wednesday, June 6, 2001 4:16 AM

> > 6 - Since we have another AI about 'private with', should we add an
> > 'abstract private with' too?  (Yuck!)
>
> Yes, of course. Indeed, I would expect that to be the most common use, as
> you generally need those pointers at other objects in the full type
> declaration of the new object. (Why the yuck??)

I say "yuck" whenever I see a string of more than two reserved words.  Hence:

    type T is abstract tagged limited private; -- Yuck, yuck, yuck

> A more interesting question is whether we can have private abstract
> packages. If we can, can the privateness of the abstract and the
> specification be different? If we can't have private abstract packages, why
> not??

I'd say that abstracts are useful for private packages, but I don't think that
the privateness should be different for the abstract and for the real thing.

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

From: Tucker Taft
Sent: Tuesday, June 5, 2001  10:09 PM

Perhaps the simplest approach is to introduce the notion of, say, a
"limited with" clause, and a "limited compilation" of a package.
A limited compilation of a package creates an entry in the environment
for the package (a "limited package" henceforth) which contains
only incomplete [tagged] types, abstract interface types, limited subpackages,
and general access types.  A limited with clause makes only these items
visible. It makes all types declared in the visible part of the package
visible, but generally as incomplete [tagged] types, unless they happen to be
general access types whose designated subtype is a first subtype that is
declared in the same package or an ancestor, in which case they are not
incomplete, and can be used for objects and parameters, but cannot be used for
an allocator, a dereference, or as an actual type in an instantiation.

Elaborating a limited package has no effect.  A limited package
has dependences only  on limited versions of its ancestor units
(i.e., all "with"s of the package are ignored).

A limited with of a child package implicitly performs a limited with on each
of its ancestors as well.

This would make the above example even simpler.  Just change the "abstract
with" to "limited with" and drop all the "abstract ... end abstract;" junk.

Obviously for source-based compilers, a "limited compilation" is pretty trivial.
My sense is that this is pretty much what GNAT is already doing when
it sees a "with type" clause.  I objected to having to have the source for the
spec available when the "with type" is processed, in part because I couldn't
figure out how this could be explained in terms of a more "conventional" Ada
library model.

However, if we introduce the notion of a limited compilation I find the
requirement to have the spec around a bit more acceptable.  This
creates the equivalent of the "package abstract" but only
as an internal structure, not as a separate source file.  Furthermore, there is
no need to perform a new "limited compilation" if a spec changes in a way that
is compatible with the existing limited package.  I.e., one would do the
basic conformance check when the spec is "really" compiled against what
was retained when the limited compilation occurred, and so long as no new
types showed up, and the special general access types remained compatible,
all remains hunky dory.

Of course, the source-based compilers already handle this kind of mutually
recursive compilation dependence because of mutual inlining.

I realize that we have plowed this ground before, but I find the "with type"
thing just a bit too etherial, and am concerned that we need a good model
for what happens both in the source-based model and the conventional library
model, and what happens when multiple "with types" are in scope for the
same package.  The idea of a limited package is better in this regard because
a given package has exactly one "limited" representation, so no matter how
many "limited with"s are around, there is no need to create a "merged" package.
Either you get the whole package, or you get the limited version, and there is
only one of those.  Also, the limited package is a conceptual library item,
which gets elaborated at a specific time.

Anyway, there is more food for thought.  I will be happy to write up
this limited package concept if it seems more attractive than the package
"abstract."

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

From: Pascal Leroy
Sent: Wednesday, June 6, 2001 4:12 AM

Your "simplest approach" looks frighteningly complicated to me.  Now we need to
scan a package specification, and only process those declarations that are part
of the limited package.  And we need to store in the library a Diana tree which
has been only partially decorated.  The last sentence of the above paragraph
(about general access types) is especially worrisome as it seems to imply that
name resolution took place (to locate the designated subtype) but that no other
processing was done (legality checking on the designated subtype).  Certainly
our compiler doesn't work that way.

I don't even want to start thinking of the interaction with incremental
compilation.  Does anyone have aspirin?

How about the following model, going back to the notion of abstract part, with
a generic-like syntax:

    abstract
        type Dept;
        type Dept_Ptr is access all Dept;
    package Department is
        type Dept is private;
        ...
    private
        type Dept is ...;
    end Department;

1 - An 'abstract with' clause creates a semantic dependency on the abstract
of a package.

2 - Adding an abstract to the environment creates an entry for the package in
the library, and causes the compiler to process only the abstract_items (i.e.,
the declarations between 'abstract' and 'package').

3 - Adding a package to the environment causes the compiler to process the
entire specification, including the abstract_items.  If there is already an
abstract in the library, and the new one does not conform to the old one, the
compiler is allowed to remove the compilation units which depend on the old
abstract.  Alternatively, the inconsistency may be detected as a
post-compilation check.

4 - No abstract part on nested packages: this is to avoid the situation where
abstract_items would depend on preceding (non-abstract) declarations in the
same library unit, which would make the compilation of an abstract much more
complicated.

5 - The above syntax easily accommodates abstracts of private package, if we
want such a thing (that seems very reasonable to me).

While this mechanism would have a significant impact on the computation of the
compilation order, it is, as you point out, not too different from what has to
be done for mutual inlining.

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

From: Tucker Taft
Sent: Wednesday, June 6, 2001  8:47 AM

I like it.

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

From: Randy Brukardt
Sent: Wednesday, June 6, 2001  2:48 PM

So do I. It's certainly better than the Advil-inducing proposal that
preceded it.  Looks to me like Pascal volunteered to write this AI. (Nudge,
nudge :-)

Anyway, I'd suggest making an attempt to write this up, in order to see what
other problems come up.


Some questions to answer:

1) Does the abstract part go before or after the generic part for generic
packages. (Probably after??)

2) This does seem to imply a new kind of environment entity - a
abstract-only package. Moreover, it insists that the compiler can check
these for conformance and  require replacement only when it does not change.
Not all compilers make any attempt at checking for changes in units -- it
often is counter-productive (it's faster to just recompile everything).
Indeed, the wording/notes would need to make it very clear that compiling a
full specification CANNOT obsolete ("remove from the environment") units
that depend on the abstract UNLESS the abstract does not conform. Else we
haven't gained anything.

3) What exactly can be put into the abstract part? Probably this would be
essentially the same as the current proposal.

4) This proposal would require allowing incomplete types to be "completed"
with a private type. This currently is illegal, and we would have to be
careful with freezing rules (or at least, freezing implementation). That is,
Pascal's example:

     abstract
         type Dept;
         type Dept_Ptr is access all Dept;
     package Department is
         type Dept is private;
         ...
     private
         type Dept is ...;
     end Department;

would be illegal if written without the abstract part in Ada 95:

     package Department is
         type Dept;
         type Dept_Ptr is access all Dept;
         type Dept is private; -- <<ILLEGAL.
         ...
     private
         type Dept is ...;
     end Department;

So this clearly needs a full write-up. Also, I will need to know if the new
write-up is a replacement for the current "abstract package" proposal
(AI-271), or if it is a third separate proposal (I hope not!).

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

From: Tucker Taft
Sent: Wednesday, June 6, 2001  10:39 AM

Randy Brukardt wrote:
> ...
> Of course, if the "abstract part" idea is adopted (with better syntax!),
> this all becomes moot: the visibility is always on the parent full spec, and
> the child cannot be mutually recursive with the parent (because that would
> make the parent dependent on its own full definition).

Actually, I think it is simpler to say that the abstract
part of a child only sees the abstract part of its ancestors
(if any).  Similarly, an "abstract with" (or should it be
"with abstract") gains visibility on the abstract part of
all packages mentioned (if any).  The abstract part gains
no visibility via "with" clauses.  It is intended to be
largely self-contained, the only exception being visibility
on the abstract part of its ancestors.

This approach ensures that all abstract parts can be
"elaborated" before anything else, in a top-down order.
It means that a child abstract can be used to hold the
access type to a type declared in a parent abstract, to
keep the parent "pure" if desired.  But otherwise,
access types declared in an abstract part would always
refer to incomplete types declared in the same abstract part.

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

From: Pascal Leroy
Sent: Wednesday, June 6, 2001  3:04 PM

> Similarly, an "abstract with" (or should it be
> "with abstract")

To my non-native ears, "with abstract" somehow sounds better, since we
decided to use abstract as a noun in this context.

> This approach ensures that all abstract parts can be
> "elaborated" before anything else, in a top-down order.

That seems like a useful invariant to maintain, as it minimizes the impact
on the algorithm that computes elaboration order.

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

From: Tucker Taft
Sent: Wednesday, June 6, 2001  4:35 PM

> > Similarly, an "abstract with" (or should it be
> > "with abstract")
>
> To my non-native ears, "with abstract" somehow sounds better, since we
> decided to use abstract as a noun in this context.

I tend to agree.  Also, since we are thinking in terms of an
abstract "part," an "abstract with" would appear to be a with
clause used by the abstract part, rather than a with clause
that makes some other package's abstract part visible.
Perhaps even "with P abstract, Q, R abstract;" would be the right
answer.

> > This approach ensures that all abstract parts can be
> > "elaborated" before anything else, in a top-down order.
>
> That seems like a useful invariant to maintain, as it minimizes the impact
> on the algorithm that computes elaboration order.

Essentially, it means you can completely ignore them, since cycles
are impossible, and there is no run-time effect.

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

From: Tucker Taft
Sent: Wednesday, June 6, 2001  4:39 PM

I would be willing to rewrite AI-271 to use this new approach,
or would also be willing to cede my authorship to Pascal.
It's up to you, Pascal...

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

From: Pascal Leroy
Sent: Thursday, June 7, 2001  2:50 AM

> 1) Does the abstract part go before or after the generic part for generic
> packages. (Probably after??)

I don't think abstract parts make sense for generics.  A generic doesn't
export anything (it's the instance that does) so how can it be part of a
circular relationship.  An the instance has exactly the same dependencies as
the generic, so it cannot be part of a cycle either.

Ok, there is the case of children of generics, which have visibility over
the stuff declared in their parent.  But I was under the impression that we
had killed the notion of having circularities between a child and its parent
even in the non generic case.

Or am I missing something?

> 2) This does seem to imply a new kind of environment entity - a
> abstract-only package.

That's not how I would phrase it, I would say a package for which only the
abstract has been entered in the environment.  That way, we preserve a
1-to-1 correspondence between compilation units and "environment entities".
In implementation terms, of course, you can do what you want.

> Moreover, it insists that the compiler can check
> these for conformance and  require replacement only when it does not
change.
> Not all compilers make any attempt at checking for changes in units -- it
> often is counter-productive (it's faster to just recompile everything).
> Indeed, the wording/notes would need to make it very clear that compiling
a
> full specification CANNOT obsolete ("remove from the environment") units
> that depend on the abstract UNLESS the abstract does not conform. Else we
> haven't gained anything.

Absolutely.  I believe that this followed from the way I wrote the rules,
but it definitely worth spelling it out in a note.

> 3) What exactly can be put into the abstract part? Probably this would be
> essentially the same as the current proposal.

Yes.

> 4) This proposal would require allowing incomplete types to be "completed"
> with a private type. This currently is illegal, and we would have to be
> careful with freezing rules (or at least, freezing implementation). That
is,
> Pascal's example:

Right, the rules would require modification.  Note that now that the
abstract part and the rest of the specification are in the same unit, we
probably don't need to require that each and every abstract_item be repeated
(in a conformant manner) in the specification.  The only requirement is that
each incomplete type in the abstract_items must be completed in the spec.

> So this clearly needs a full write-up. Also, I will need to know if the
new
> write-up is a replacement for the current "abstract package" proposal
> (AI-271), or if it is a third separate proposal (I hope not!).

I'd say that it replaces AI-271.

I am willing to try and write it up (I suppose I'll buy some consulting from
Tucker).

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

From: Jean-Pierre Rosen
Sent: Thursday, June 7, 2001  8:21 AM

> > 1) Does the abstract part go before or after the generic part for generic
> > packages. (Probably after??)
>
>...
> Or am I missing something?

Maybe the case of a generic formal package ?

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

From: Pascal Leroy
Sent: Thursday, June 7, 2001  10:20 AM

That vaguely crossed my mind.  I could imagine that package P would have a
generic formal package which is an instantiation of Q, and Q would have a
generic formal package that is an instantiation of P.  But that seems an
altogether different problem, which would probably require a different
solution, and more importantly for which we have no indication that it
matters to real users (as opposed to language lawyers).

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

From: Randy Brukardt
Sent: Thursday, June 7, 2001 12:40 PM

Well, this was precisely the same sort of argument that was used to not
bother with children of generics. And eventually that shot down, and so we
have these awful generic children of generic units.

I doubt that the larger community (which Dave mentioned ought to be
involved) would be happy with restrictions on circularities between a parent
and its children, or on use of these things with generics. That doesn't mean
that we need to provide those capabilities, but we do need to be prepared
for groaning...

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

From: Robert Dewar
Sent: Tuesday, June 12, 2001 8:21 PM

<<   -- Because the GNAT implementation rules only work on a "source-based"
implementation;
>>

I don't see that at all, can you explain?

<<   -- Because the GNAT implementation only works if there is only one
representation for access types.
>>

I don't see this either ...

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

From: Pascal Leroy
Sent: Wednesday, June 123 2001 3:16 AM

> <<   -- Because the GNAT implementation rules only work on a "source-based"
> implementation;
> >>
>
> I don't see that at all, can you explain?
>
> <<   -- Because the GNAT implementation only works if there is only one
> representation for access types.
> >>
>
> I don't see this either ...

Robert, I invite you to take a look at the minutes of the ARG meetings help at
AverStar (Mar 99), TopLayer (Sep 99) and Columbia (Nov 00).  Look for AI 217
(well, in the AverStar minutes you want to look for AI 216 because there appears
to be a glitch in the numbering).  Essentially, I am too lazy to write a 4-page
message on these issues.

Note that these various meetings were attended by Ed and Gary, and I don't think
the reality of the issues mentioned above was seriously in doubt.  On the other
hand, I distinctly remember Gary being quite unhappy with the "solutions" that
the ARG was advocating to deal with these issues.

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

From: Robert Dewar
Sent: Wednesday, June 13, 2001 9:47 AM

OK, I see what you mean regarding the source model only, that makes sense.

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

From: Robert Dewar
Sent: Wednesday, June 13, 2001 9:47 AM

Actually for GNAT, the situation is really neutral. We will in any case
keep our implementation of with type (under the extension flag), and if
some new mechanism is implemented, we will consider implementing it if
and only if there is customer demand (as for any other extension).

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

From: Jean-Pierre Rosen
Sent: Wednesday, June 13, 2001, 10:26 AM

Only syntactic sugar, but sometimes sugar helps the medicine go down....

Rather than
   with abstract P;
I suggest
   with abstract of P;

Otherwise, it looks like if P were an "abstract package".

Also maybe, but it migh be more difficult to deal with, rather than
   abstract
      <declarations>
   package P is
I suggest:
   package P is
      abstract
         <declarations>
      end abstract;

Like in a paper, where the abstract is within the paper. Of course the abstract part would have to be the first declarative item. Or
do we allow pragmas before ? use clauses ? hmm.....

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

From: Ed Schonberg
Sent: Wednesday, June 13, 2001, 11:01 AM

In that case, why not an attribute?

      with P'Abstract;

It's some characteristic of the package, after all. In particular with
JPR's second suggestion that the package start with an optional abstract.

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

From: Tucker Taft
Sent: Wednesday, June 13, 2001, 11:23 AM

Interesting...  This actually looks nice to my eyes.

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

From: Tucker Taft
Sent: Wednesday, June 13, 2001, 11:06 AM

> Attached is my update to AI 271, following the recent discussion on this
> topic.

Thanks for doing this.  Comments below.

-----------

> !standard 07.01   (02)                                01-05-31  AI95-00271/02
> !class amendment 01-05-31
> !status work item 01-06-13
> !status received 01-05-31
> !priority Medium
> !difficulty Hard
> !subject Handling mutually recursive types via package "abstracts"
>
> !summary
>
> A new construct, called a package "abstract," is proposed as a potential
> solution to the "mutually recursive types across packages" problem.
> This is an alternative to the "with type" proposal of AI-217.
>
> A package "abstract" is an extract of a package specification, containing
> types, and potentially subpackages, representation items, and static
> constants that may be needed to allow two packages to declare mutually
> recursive types.  A package abstract is referenced by an "abstract with"
> clause.  A package specification must conform to its package abstract,
> if any.
>
> !problem
>
> Ada only allows mutually recursive types to be declared 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.
>
> !proposal
>
> Three new constructs are proposed: a package_abstract, a with_abstract
> clause, and an incomplete tagged type.
>
>
> PACKAGE ABSTRACT:
>
> A package_abstract has the following syntax:
>
>     package_abstract ::= library_package_abstract |
>                          nested_package_abstract
>
>     library_package_abstract ::=
>         ABSTRACT
>             {abstract_item}
>
>     nested_package_abstract ::=
>         ABSTRACT
>             {abstract_item}
>         PACKAGE defining_identifier ;

I'm not convinced that we need nested package abstracts.
On the other hand, if we have them, must they be in the abstract part?
Aren't they useful for local package circularity?
Of course, only if they are in the enclosing library package abstract
part would they be visible via a "with abstract" clause.

> The syntax for library_unit_declaration is amended as follows:
>
>     library_unit_declaration ::=
>         subprogram_declaration |
>         [library_package_abstract] package_declaration |
>         generic_declaration |
>         generic_instantiation
>
> In other words, a package_abstract is only allowed before a library unit
> declaration for a non-generic package.  The reason for this restriction is
> as follows:
>
>    - Subprogram_declarations do not export types or constants.
>
>    - Generic_declarations and generic_instantiations cannot participate in
>      circular dependencies.
>
>    - The abstracts for nested packages are given as nested abstracts in the
>      top-level abstract.  That's because we want abstracts to be entered
>      "early" in the environment.  If nested packages had abstracts, they
>      would have to be entered "early" too.  But then they could depend on
>      declarations appearing earlier in the same library unit declaration,
>      so this library unit declaration would have to be partially processed
>      by the compiler, and we don't know how to specify this in a way that
>      works for both source-based and library-based compilers.
>
> A package abstract may contain only the following kinds of declarative items:
>
>    abstract_item ::=
>        incomplete_type_declaration
>      | abstract_interface_declaration    -- see AI-251 on interface types
>      | TYPE defining_identifier IS access_type_definition ;
>      | defining_identifier_list : CONSTANT [subtype_indication]
>          := static_expression ;
>      | attribute_definition_clause
>      | nested_package_abstract
>
> Representation pragmas are also permitted.
>
> No pool-specific access types may be declared in a package abstract.
>
> All expressions appearing in a package_abstract must be static.
>
> The specification of a package must "conform" to its abstract, if any,
> in the following sense:
>
>    - Each incomplete type declaration must be completed in the visible
>      part of the specification of the package by a full_type_declaration,
>      a private_type_declaration, or a private_extension_declaration.
>
>    - For each nested_package_abstract, there must be a
>      package_specification with the same defining_identifier in the
>      visible part of the specification of the package.  This package must
>      in turn conform to its abstract.  (This wording is probably not quite
>      right, but the purpose should be clear.)
>
> If an access type is declared in a package abstract, all aspects of
> its representation except for its storage pool are frozen at the end
> of the package abstract.  As indicated above, the access type
> must not be pool-specific.
>
> The only representation items permitted in the package specification
> for the access type are those that apply to the storage
> pool of the type (Storage_Pool, Storage_Size).
>
> The elaboration of a package abstract has no effect.
>
> The actual "collection" for the access type is not created until
> the package specification is elaborated.  An allocator is illegal for an
> access type declared in an abstract, though not for the corresponding
> access type declared in the specification.

There is no "corresponding" access type declared in the specification.
Perhaps you mean that an allocator for an access type is only legal if the
enclosing package's specification is visible.  If only the enclosing
abstract part is visible, then no allocators.

> ... Similarly, an access
> type in an abstract must not be passed as an actual for a formal
> access type in a generic instantiation.

.. unless the enclosing package's specification is visible.

> WITH ABSTRACT CLAUSE:
>
>    with_clause ::= with_abstract_clause | with_unit_clause
>
>    with_unit_clause ::= WITH library_unit_name {, library_unit_name}
>
>    with_abstract_clause ::=
>        WITH ABSTRACT library_unit_package_name {, library_unit_package_name};
>
> [Anyone has a better name for with_unit_clause?]

Perhaps "with_declaration_clause" or "with_specification_clause" or
"with_visible_clause".

Did you consider associating the word "abstract" with individual
library_unit_package_name's, rather than with the clause as a whole?
Alternatively, we could only allow one package per "with abstract" clause,
to avoid any confusion.  I had (semi-seriously) suggested putting
the word "abstract" after the package name, so that it
would (perhaps) read better, and be more clearly associated with
each package, rather than with the clause as a whole.

> The scope of a with_abstract_clause that appears on a
> library_unit_declaration which is a package_declaration with a
> library_package_abstract consists only of the declarative region of the
> package_declaration.  The declarative region of the
> library_package_abstract is not included.  [Otherwise we are back to a
> circularity where two packages that have abstracts cannot depend on
> each other.]

Agreed.

> The scope of a with_unit_clause that appears on a
> library_unit_declaration which is a package_declaration with a
> library_package_abstract consists of the declarative region of the
> library_package_abstract (which in turns include the package_declaration,
> etc.).

I don't think we want *any* "with" clauses to apply to package abstracts.
I thought we agreed we wanted these to be elaborated before everything
else, and the only thing they could "see" would be the abstracts of
their ancestors.

> A with_abstract_clause on a compilation unit makes the abstract (and only
> the abstract) of each specified package visible within the compilation
> unit.  It has no effect if the specification of the package is visible due
> to a with clause on some ancestor (or corresponding declaration) of the
> compilation unit.

Wouldn't it be better to make a "with_abstract" override an inherited
"with_unit" clause?  That way if the inherited "with_unit" clause is
later removed, you wouldn't get any nasty surprises.

> A with_unit_clause on a compilation unit makes both the abstract and the
> visible part of each specified package visible within the compilation unit.
>
> Within the abstract for a child package, the abstract of its ancestors,
> if any, is directly visible.

... the abstract{s} of its ancestors, if any, {are}[is] directly visible.

> If a package specified by a with_abstract_clause on some compilation unit
> is a child package, then the abstract of its parent and each further
> ancestor is made visible as if it were denoted by an explicit
> with_abstract_clause.

Agreed.

> If an abstract or specification of a package is made visible by an abstract
> with clause, then a semantic dependence is created on that part of the
> package.  [This in turn implies an elaboration dependence -- see 10.2(9).]
> The specification of a package has a semantic dependence on its abstract,
> if any.

You need to make clear first that the abstract of a package will generally
be elaborated at a separate time from when the specification of the
package is elaborated.

> The compilation model for abstracts is as follows:
>
>    - Adding a package_abstract to the environment causes the compiler to
>      process only the library_package_abstract part of the unit.

I think the word "adding" is confusing here.  Perhaps you might want
to say "Submitting a package abstract to the compiler ..."

>    - Adding a library_unit_declaration which includes a package_declaration
>      to the environment causes the compiler to process both the
>      library_package_abstract and the package_declaration.

Again, I think the word to be used here is "submitting."
>
>    - When a library_unit_declaration is compiled, and the environment
>      already contains a package_abstract for the same unit, and that
>      abstract doesn't conform to the abstract of the unit being compiled,
>      the compiler may remove the existing package_abstract from the
>      environment.  It is _not_ allowed to do this if the
>      library_unit_declaration has a package_abstract which conforms to
>      the one already entered in the environment.

Here is where I think you should talk about "when blah is added to the
environment...".

If you look at the wording in 10.1.4, I think you have used "added"
when 10.1.4 typically talks about "submitting," and vice-versa.

>    - Post-compilation rule: it shall not be possible to run a partition
>      that contains a package_abstract and a package_specification which do
>      not conform to each other.

I think you don't want this to wait until you "run" the partition.
This should be checked when compilation units are "included" in the partition,
corresponding to 10.2(27).  It may be that we simply need to
extend the definition of "consistency" and then rely on 10.2(27).

Which brings up the point -- is a package abstract (conceptually) a
separate "compilation unit," which just happens to require that it be
immediately followed by a corresponding package specification
compilation unit?  I think that might be the right (conceptual) model,
though we don't expect them to ever be in physically separate "files"
(whatever those are ;-).

> INCOMPLETE TAGGED TYPE:
>
>    incomplete_type_declaration ::=
>        TYPE defining_identifier [discrminant_part] [IS TAGGED];
>
> An incomplete tagged type may be used as the type of a formal parameter
> prior to its completion, in addition to the normal places where
> an incomplete type may be used.  Also, the Class attribute is defined for
> an incomplete tagged type.  The class-wide type denoted by the Class
> attribute of an incomplete type may also be used as the type of a
> formal parameter.  The Class attribute of a non-tagged incomplete
> type would be considered obsolescent.
>
> Note that we are not proposing that an incomplete tagged type may be
> used as a return type, because of the many special rules and implementation
> issues associated with returning tagged types (e.g. functions becoming
> abstract, accessibility checks on returning limited by-reference types,
> dispatching on result coupled with tag indeterminacy, finalization associated
> with returning potentially controlled types, etc.)
>
> !discussion
>
> The concept of a package abstract has been discussed before, under
> different guises.  At the time, the "with type" seemed somewhat more
> attractive.  However, various technical difficulties associated with
> making access types visible via "with type" have reduced the
> capability of the "with type" proposal.  Also, implementation discussions
> about how to implement "with type" have revealed some significant issues
> associated with whether or not the specification must be in the
> environment prior to compiling a "with type" clause.  Other problems
> that have arisen relate to the need to implicitly create an "abstract"
> for a package to implement "with type," with the attendant issues associated
> with implicit structures in the program library, their longevity, their
> consistency, etc.  Finally, "with type" requires creating some rather
> strange "fictions" to explain to the user what is really going on.
>
> There seem to be two general ways this kind of problem is solved in
> other languages.  One approach is to permit unrestricted forward
> references.  This is the approach adopted by Eiffel, Java, and Smalltalk.
> The other approach is to require "forward" or "incomplete" declarations,
> sometimes called "signatures" or "partial revelations."  This is generally
> the approach adopted by Ada, as well as Pascal, Modula, ML, C/C++, etc.
> The notion of a package "abstract" is analogous to the "incomplete"
> type in Ada, the signature in ML, and the partial revelation in Modula.
>
> The "with type" approach seems to be a bit of an awkward half-way
> position, allowing a kind of "restricted" forward reference, lacking
> the generality of unrestricted forward reference, and lacking the
> flexibility of a true "forward" declaration, where representation
> items and other capabilities can be accommodated.
>
> The package abstract approach seems more natural for Ada, and gives
> the user something to look at and conceptually "hold onto," which
> should make the concept easier to understand.
>
> We considered separating the abstract and making it a new kind of
> library unit.  However, this would have a significant impact on the
> tools which deal with the enviroment and the library units, in addition to
> the impact on the compiler proper.  By putting the abstract in the same
> library unit as the specification, we are avoiding the extra burden of
> a new kind of unit.  However, we need to define a two-phase compilation
> process, where the abstract is entered in the environment on a reference
> through a with_abstract_clause, and the specification is entered in the
> environment on a reference through a normal with clause.

I think this use of "enter into the environment" is confusing.
In general we talk about with clauses making things (in the environment)
visible, rather than causing them to be entered into the environment.
E.g., see 8.3(20), 10.1.4(2), and 10.1.6(2).
Units are entered into the environment as a side effect of
submitting them to the compiler, or by some other extra-lingual
means.  Also see 10.1.4(7).

> >From an implementation point of view, the "optionality" of a package
> abstract is somewhat analogous to the "optionality" of a subprogram
> specification.  The two-phase compilation process is somewhat analogous to
> the two-phase process that has to take place to support mutual inlining
> (an optional capability).
>
> !example
>
> 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.
>
>     with abstract Departments;
>     abstract
>         type Employee;
>         type Emp_Ptr is access all Employee;
>     package Employees is
>         type Employee is private;
>         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
>                 Assigned_To : Departments.Dept_Ptr;
>             end record;
>     end Employees;
>
>     with abstract Employees;
>     abstract
>         type Department;
>         type Dept_Ptr is access all Department;
>     package Departments is
>         type Department is private;
>         procedure Choose_Manager(D : access Department;
>                                  Manager : access Employees.Employee);
>         ...
>     private
>         type Department is
>             record
>                 Manager : Employees.Emp_Ptr;
>             end record;
>     end Departments;
>
> !ACATS test

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

From: Ed Schonberg
Sent: Wednesday, June 13, 2001, 11:31 AM

Tuck writes:

>  Did you consider associating the word "abstract" with individual
>  library_unit_package_name's, rather than with the clause as a whole?
>  Alternatively, we could only allow one package per "with abstract" clause,
>  to avoid any confusion.  I had (semi-seriously) suggested putting
>  the word "abstract" after the package name, so that it
>  would (perhaps) read better, and be more clearly associated with
>  each package, rather than with the clause as a whole.

If we regard the abstract as a representation item, then the attribute
notation can be used both in the with_clause and the abstract declaration:

   with P'Abstract, Q'abstract;
   package Client is...

   for P'Abstract use
     type T1;
     ...
   end package;

A representation pragma is allowed where a compilation unit is allowed,
so the pragma can be introduced in the environment independently of the
package itself, as desired. Or if we prefer, the representation pragma
could appear as the first item in the package specification. The
semantics are unaffected.

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

From: David Emery
Sent: Wednesday, June 13, 2001, 12:15 PM

Ooh!  Looks good!  It has a lot of "sematics by analogy"
with representations, so the relatively experienced Ada
programmer should grok this pretty quickly.

I've been 'auditing' the discussion, and I think we're
getting close to a good, elegant solution that will be
easy to "sell" to the existing user community.

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

From: Tucker Taft
Sent: Wednesday, June 13, 2001, 12:17 PM

Gack.  I liked the "with P'Abstract," but I can't say I like this
next step.  The reasons for attaching the abstract to the front of
the package spec were two-fold:

   1) No new compilation unit type, forcing GNAT and others to go
      beyond the "*.ads, *.adb" or "*.1.ada, *.2.ada" or "*.spc, *.bdy"
      nomenclature.

   2) Easy to parse just the "front" of a file for the purpose of determining
      compilation order, and for creating package abstracts in the program
      library.

I think the rep-clause approach would defeat one or both of these goals
(and aesthetically, my reaction is pretty negative... ;-).

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

From: Ed Schonberg
Sent: Wednesday, June 13, 2001, 12:39 PM

In that case, following JPR's suggestion, the representation clause could
be immediately within the package specification. It would be sufficient to
parse the beginning of the file, and there are no new compilation unit
kinds to handle:

     package P is
       for P'abstract use
          type T;
          ..
       end abstact;
       type T is..
    end P;

Still, the advantage of the compilation-level rep clause is that it is possible
to introduce the abstract before the package proper, which seems like
a nice instance of decision postponement!

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

From: Robert Dewar
Sent: Wednesday, June 13, 2001, 2:21 PM

<<>     1) No new compilation unit type, forcing GNAT and others to go
>      beyond the "*.ads, *.adb" or "*.1.ada, *.2.ada" or "*.spc, *.bdy"
>      nomenclature.
>>

I don't think that's a significant issue for GNAT (indeed a planned
enhancement for GNAT is to allow the private part of a package to be
in a separate file anyway).

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

From: Robert Dewar
Sent: Wednesday, June 13, 2001, 2:12 PM

<<   with abstract of P;
>>

UGH! For me Randy's multiple keyword allergy kicks in here.

How about

   with P abstract;

That reads perfectly fine in english (abstract being a noun here, and the
package name in apposition), and cannot possibly be confused with an
abstract package.

... hmmm! it's SO much easier to discuss surface syntax ... :-)

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

From: Pascal Leroy
Sent: Wednesday, June 13, 2001, 2:12 PM

>      with abstract P;
>      with abstract of P;
>      with P'Abstract;

I could live this either of these, although I have a preference for #2
(Jean-Pierre's proposal).  I am not convinced by the attribute, as it seems
to be inventing a whole new syntax for no good reason.  I would probably be
willing to follow Tucker and say that we can have only one package per
with_abstract_clause, to avoid confusion.

>    package P is
>       abstract
>          <declarations>
>       end abstract;

The abstract is not a nested declarative region, it is really the first part
of the package specification.  This syntax seems to imply some form of a
nested region, like we have for records or packages, and I think that's
confusing.

> If we regard the abstract as a representation item, then ...

This statement doesn't make any sense to me.  The abstract is a declarative
region, with special compilation ordering properties.  How can you regard it
as a representation item?  In fact the abstract doesn't necessarily define
representation aspects, its most important role is to declare
names/entities.  So I think that the analogy is misguided, certainly I fail
to see the "semantics by analogy".  The more convincing similarity to me is
that the abstract is a kind of restricted visible part.

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

From: Ed Schonberg
Sent: Wednesday, June 13, 2001, 2:52 PM

>  I am not convinced by the attribute, as it seems
>  to be inventing a whole new syntax for no good reason.

Au contraire! An attribute does not invent any new syntax. "Abstract of"
is definitely a new production.

>  > If we regard the abstract as a representation item, then ...

>  This statement doesn't make any sense to me.

I think there are advantages in thinking of it as a representation item.
First, it's easy to say that its support is implementation-defined :-)!
Second, it is a characteristic of the package that we choose to expose
or not. If the package possesses this characteristic it can participate
in mutually recursive type declaration. This characteristic of the
package has the useful property that its elaboration does nothing.

If we take JPR's view of an abstract seriously, i.e as a summary of what
follows, it seems confusing to me to treat it as a declarative region.
In particular, I suppose that I am allowed to have an incomplete type
declaration in the abstract, for a private type that appears in the
subsequent visible part of the package, right?

If we allow the abstract to be a compilation item, the argument is more
compelling: it can be added a posteriori to existing packages, without
affecting their clients, and allowing them to participate in mutually
recursive declarations. There are no new entities introduced, just new
visibility properties of the package. Sounds like representation to me.

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

From: Pascal Leroy
Sent: Wednesday, June 13, 2001, 3:25 PM

> Au contraire! An attribute does not invent any new syntax. "Abstract of"
> is definitely a new production.

Well, there is new syntax at any rate, because if you look at the current
grammar, the unit name cannot be an attribute...

I recall that attributes-that-are-reserved-words are a bit of a pain in our
parser, although it's probably not too severe here, since this attribute
could only appear in this particular context (I am assuming that you are not
proposing that P'Abstract be usable in any context where a unit name is
appropriate; that would be bad).

> In particular, I suppose that I am allowed to have an incomplete type
> declaration in the abstract, for a private type that appears in the
> subsequent visible part of the package, right?

Right.  You can think of the incomplete type as a kind of partial-(partial
view).

> If we allow the abstract to be a compilation item, the argument is more
> compelling: it can be added a posteriori to existing packages, without
> affecting their clients, and allowing them to participate in mutually
> recursive declarations. There are no new entities introduced, just new
> visibility properties of the package. Sounds like representation to me.

Sorry, I remain skeptical, added visibility doesn't sound like
representation to me.

> How about
>
>    with P abstract;
>
> That reads perfectly fine in english (abstract being a noun here, and the
> package name in apposition), and cannot possibly be confused with an
> abstract package.

Or maybe, combined with Ed's suggestion, assuming that we add a new
attribute 'S:

    with P's abstract; -- Just kidding

Seriously, "with P abstract" is fine, and I am ready to defer to the greater
wisdom of native speakers as to what sounds best.  But I still think that we
want to follow Tuck's suggestion, and have exactly one package name.  A
clause like:

    with P abstract, Q, R abstract, S abstract;

looks rather confusing to me, as it's hard to figure out which are normal
withs and which are with_abstracts.

> ... hmmm! it's SO much easier to discuss surface syntax ... :-)

Indeed.

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

From: Randy Brukardt
Sent: Wednesday, June 13, 2001, 5:21 PM

> >  I am not convinced by the attribute, as it seems
> >  to be inventing a whole new syntax for no good reason.
>
> Au contraire! An attribute does not invent any new syntax. "Abstract of"
> is definitely a new production.

Perhaps not technically, but practically most compilers would have to add
syntax for this, since Abstract is a reserved word. If we used a
non-reserved word, it would work a bit better:

    with P'Partial;

Moreover, Janus/Ada at least uses a grammar where "Unit_Name" is restricted
to a sequence of identifiers:

   With_Stmt ::= WITH Only_Unit_Name_List SEMICOLON ## 128
   Only_Unit_Name_List ::= Unit_Name ## 134
      | Only_Unit_Name_List COMMA Unit_Name ## 134
   Unit_Name ::= IDENTIFIER ## 114
            | Unit_Name DOT IDENTIFIER ## 114

So allowing this to be an attribute would be a significant grammar change.

> First, it's easy to say that its support is
> implementation-defined :-)!

Why in heavens name would we want to say that? This doesn't have much value
unless most compilers implement it, and in exactly the same way.

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

From: Pascal Leroy
Sent: Thursday, June 14, 2001, 3:31 AM

> Moreover, Janus/Ada at least uses a grammar where "Unit_Name" is restricted
> to a sequence of identifiers:
>
>    With_Stmt ::= WITH Only_Unit_Name_List SEMICOLON ## 128
>    Only_Unit_Name_List ::= Unit_Name ## 134
>       | Only_Unit_Name_List COMMA Unit_Name ## 134
>    Unit_Name ::= IDENTIFIER ## 114
>             | Unit_Name DOT IDENTIFIER ## 114
>
> So allowing this to be an attribute would be a significant grammar change.

We have essentially the same grammar (not too surprising, I guess, since it
seems to be the obvious way to do it).  Note however that if you look at
Ed's and Robert's proposals:

    with P'Abstract;
    with P abstract;

you notice that they differ only by the replacement of a quote by a space,
so I suspect that they have the same implementation complexity...

> Why in heavens name would we want to say that? This doesn't have much value
> unless most compilers implement it, and in exactly the same way.

My opinion exactly.  While an implementer is free to ignore what the ARG
does if it doesn't make business sense (heck, an implementer doesn't even
have to validate these days), the purpose of the ARG is not to come up with
complex language amendments only to conclude, By the way, this all
implementation-defined.

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

From: Robert Dewar
Sent: Thursday, June 14, 2001, 6:47 AM

In fact the ARG should not come up with anything that it is not fairly
sure will get implemented by at least two major vendors (if its only one,
then they could just do it themselves :-)

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

From: Randy Brukardt
Sent: Wednesday, June 13, 2001, 5:08 PM

> >     1) No new compilation unit type, forcing GNAT and others to go
> >      beyond the "*.ads, *.adb" or "*.1.ada, *.2.ada" or "*.spc, *.bdy"
> >      nomenclature.
> >
>
> I don't think that's a significant issue for GNAT (indeed a planned
> enhancement for GNAT is to allow the private part of a package to be
> in a separate file anyway).

Fair enough, but Pascal indicated that it is a significant issue for
Rational (especially because of their incremental compilation technology).

I'm none to fond of an additional compilation unit type myself, but I
suspect that whatever form this proposal takes will essentially require one
(or the effect of one) anyway. That is, whether or not this proposal is
implemented as a separate unit, we're going to have to adjust compilation
order tools, compilations, etc. For instance, compiling just an abstract has
to be possible, so we end up with a mode where the parsing is artifically
stopped at some point; there is very little difference between this and a
full separate unit.

Of course, the implementation details of Janus/Ada carry only 0.01% weight
in a discussion of implementation details...

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

From: Pascal Leroy
Sent: Thursday, June 14, 2001, 3:21 AM

> Fair enough, but Pascal indicated that it is a significant issue for
> Rational (especially because of their incremental compilation technology).

Not only that, but there is also the issue of all the other tools in the
environment (in particular the Ada editor) which would have to know about
the new kind of compilation unit.  That would be _a_lot_of_work_ for us.

> I'm none to fond of an additional compilation unit type myself, but I
> suspect that whatever form this proposal takes will essentially require one
> (or the effect of one) anyway. That is, whether or not this proposal is
> implemented as a separate unit, we're going to have to adjust compilation
> order tools, compilations, etc. For instance, compiling just an abstract has
> to be possible, so we end up with a mode where the parsing is artifically
> stopped at some point; there is very little difference between this and a
> full separate unit.

That's true from an implementation perspective, of course, but I have this
feeling that from a user perspective it makes a big difference.  Adding a
new kind of compilation unit is a very visible change to the language;
adding another declarative region to a specification probably looks more
"minor".  Of course, this is just a subjective argument, but it might help
when selling the idea to the user community.

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

From: David Emery
Sent: Thursday, June 14, 2001, 6:44 AM

Speaking informally as a "user", what's key for this is
to allow a reasonably knowledgable user to get his/her
"hands around" the new feature.  Most users are not particularly
concerned with the kinds of library units (although many of
us get annoyed when the tools tell us how we should name
our files).

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

From: Robert Dewar
Sent: Thursday, June 14, 2001, 7:30 AM

We really should have implementation advice that says that Ada 95 compilers
should NOT place restrictions on file names for source files :-)

I am surprised how often we encounter customers maintaining compatibility
with compilers that have very strenuous naming rules, and in practice this
is definitely a portability issue.

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

From: David Emery
Sent: Thursday, June 14, 2001, 7:49 AM

I strongly agree.  And I won't even mention -which-
compiler is the most restrictive in file naming. :-)

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

From: Randy Brukardt
Sent: Wednesday, June 13, 2001, 5:30 PM

Pascal proposed:

> The scope of a with_unit_clause that appears on a
> library_unit_declaration which is a package_declaration with a
> library_package_abstract consists of the declarative region of the
> library_package_abstract (which in turns include the package_declaration,
> etc.).

And Tucker said:

> I don't think we want *any* "with" clauses to apply to package abstracts.
> I thought we agreed we wanted these to be elaborated before everything
> else, and the only thing they could "see" would be the abstracts of
> their ancestors.

Randy says:

If these are part of the package compilation unit, then we don't want the
"with" apply to the abstract part, or we we're going to have to define a lot
more abstract parts in order to get circularities:

    with B;
    abstract
        type T;
        type Access_All_T is access all T'Class;
    package A is
        type T is tagged record
           ...
           Some_B : B.Access_All_T2;
        end record;
    end A;

    with abstract A;
    package B is
        type T2 is tagged record
            Some_A : A.Access_All_T;
        end record;
        type Access_All_T2 is access all T2'Class;
    end B;

The above doesn't work if "with B" applies to the abstract part. As with
incomplete types, I view abstract parts as a necessary evil, and the fewer
of them that I have to use, the better.

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

From: Pascal Leroy
Sent: Thursday, June 14, 2001, 4:27 AM

> > I don't think we want *any* "with" clauses to apply to package abstracts.
> > I thought we agreed we wanted these to be elaborated before everything
> > else, and the only thing they could "see" would be the abstracts of
> > their ancestors.

The reason why I proposed to apply with clauses to abstracts is to make it
easier to write e.g. representation clauses:

    with System;
    abstract
        type T;
        type A_T is access all T;
        for A_T'Size use System.Word_Size;

Similarly, it might be useful to with packages containing
application-defined types (eg for discriminants) or constants (for the
various expressions that may appear in an abstract).

Note that there is another interesting visibility issue.  We said that an
abstract child doesn't have visibility over the specification of its
ancestors, only on the abstract.  This means that an abstract has no
visibility over Standard.  No universal_integer, no root_integer, no
nothing.  This looks a bit problematic to me (it certainly restricts quite
heavily the kind of expressions/names that you can write in an abstract).

> If these are part of the package compilation unit, then we don't want the
> "with" apply to the abstract part, or we we're going to have to define a lot
> more abstract parts in order to get circularities:
>
>     ...
>
> The above doesn't work if "with B" applies to the abstract part. As with
> incomplete types, I view abstract parts as a necessary evil, and the fewer
> of them that I have to use, the better.

Hmm, I guess I am not too convinced by this argument.  If two units
participate in a cycle, it doesn't seem absurd to me that they both have
abstracts.

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

From: Tucker Taft
Sent: Thursday, June 14, 2001, 5:43 AM

> The reason why I proposed to apply with clauses to abstracts is to make it
> easier to write e.g. representation clauses:
>
>     with System;
>     abstract
>         type T;
>         type A_T is access all T;
>         for A_T'Size use System.Word_Size;
>
> Similarly, it might be useful to with packages containing
> application-defined types (eg for discriminants) or constants (for the
> various expressions that may appear in an abstract).

I think allowing "with" clauses is going to add a lot of complexity.
I fear we may need to be able to distinguish those which apply to
the abstract, and those that don't (which gets us back to
"abstract with xxx" I fear ;-).  Alternatively, we find a syntactic
way to have with clauses both "before" and "after" the abstract
part.  In any case, I think only "pure" packages should be visible
in an abstract part, or else elaboration order determination
is going to get too complicated.

> Note that there is another interesting visibility issue.  We said that an
> abstract child doesn't have visibility over the specification of its
> ancestors, only on the abstract.  This means that an abstract has no
> visibility over Standard.  No universal_integer, no root_integer, no
> nothing.  This looks a bit problematic to me (it certainly restricts quite
> heavily the kind of expressions/names that you can write in an abstract).

Obviously we need to special-case Standard.   I don't see that
as a big problem.

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

From: Pascal Leroy
Sent: Thursday, June 14, 2001, 4:39 AM

> I'm not convinced that we need nested package abstracts.

I am not too convinced either, but I put them in the AI so that we discuss
the issue.  The reason why they would be useful, of course, is to have
cross-compilation-unit cycles that involve types declared in nested
packages.

> On the other hand, if we have them, must they be in the abstract part?
> Aren't they useful for local package circularity?

Local package circularity is a can of worms because then you are back to
your infamous "limited compilation" model: you have to compile the
abstracts, but nothing else.  To me, that's a no-no.

> There is no "corresponding" access type declared in the specification.
> Perhaps you mean that an allocator for an access type is only legal if the
> enclosing package's specification is visible.  If only the enclosing
> abstract part is visible, then no allocators.

Right, old wording that I inherited from your version.

> Did you consider associating the word "abstract" with individual
> library_unit_package_name's, rather than with the clause as a whole?

I guess that's Robert's proposal.  As I said, I would find mixing abstract
and non-abstract withs in the same clause very hard to read.

> Wouldn't it be better to make a "with_abstract" override an inherited
> "with_unit" clause?  That way if the inherited "with_unit" clause is
> later removed, you wouldn't get any nasty surprises.

I don't like this notion of with clauses overriding each other.  It seems to
me that with_abstract is weaker than a normal with, and therefore should
have lesser precedence, regardless of where it occurs.

> > Within the abstract for a child package, the abstract of its ancestors,
> > if any, is directly visible.

But note that this is a mess wrt Standard.

> I think the word "adding" is confusing here.  Perhaps you might want
> to say "Submitting a package abstract to the compiler ..."

Ok, I need to reword this entire section, obviously I got the words wrong,
but hopefully the intent is clear?

> Which brings up the point -- is a package abstract (conceptually) a
> separate "compilation unit," which just happens to require that it be
> immediately followed by a corresponding package specification
> compilation unit?  I think that might be the right (conceptual) model,
> though we don't expect them to ever be in physically separate "files"
> (whatever those are ;-).

I am adamantly opposed to the notion of adding more semantics to the dreaded
concept of "compilation", so I'd rather not see the abstract as a separate
compilation unit.  But then, of course, the source representation of
programs is not specified :-)

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

From: Tucker Taft
Sent: Thursday, June 14, 2001, 5:43 AM

> Local package circularity is a can of worms because then you are back to
> your infamous "limited compilation" model: you have to compile the
> abstracts, but nothing else.  To me, that's a no-no.

That was not my intent.  Local package abstracts would not be visible
unless the immediately enclosing declarations are visible.  And there
is no special "forward" reference.  You would have to split them up
like this:

procedure Test is
    abstract
        type T1;
        type T1_Ptr is access all T1;
    package local1;

    package local2 is
        type rec2 is record
            a : local1.T1_ptr;
        end record;
    end local2;

    package local1 is
        type T1 is private;
        function blah(X : T1) return local2.rec2;
        ...
    end local1;
   ...
begin
    ...
end Test;

This seems very natural to me.  Why shouldn't it work?
This doesn't require any "limited" compilation.

> I don't like this notion of with clauses overriding each other.  It seems to
> me that with_abstract is weaker than a normal with, and therefore should
> have lesser precedence, regardless of where it occurs.

I have a different view.  I don't like inheriting anything from "afar."
If you have a local "with P'abstract;" that means you want this
compilation unit to only depend on the abstract of P.  I want to
be informed if I unintentionally start referencing things not in
the abstract.  I don't want my parent unit to override this self-imposed
restriction.

I suppose I view a "with P'Abstract;" as (very) roughly equivalent to
"with P; pragma Restrict_To_Abstract(P);"  so it doesn't surprise me
that the "restrict" part takes precedence, even if there are earlier
"with" clauses.

> > > Within the abstract for a child package, the abstract of its ancestors,
> > > if any, is directly visible.
>
> But note that this is a mess wrt Standard.

As mentioned in a separate note, Standard obviously needs to be
a special case.  Perhaps all of Standard is in its abstract part,
conceptually.

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

From: Randy Brukardt
Sent: Thursday, June 14, 2001, 7:17 PM

I don't think it is a special case. Rather, it needs an abstract part defined
for it. (Possibly some of the other language-defined packages do, too.)

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

From: Robert Dewar
Sent: Thursday, June 14, 2001, 6:46 AM

I really dislike the idea of nested package abstracts, a typical examle
of cluttering an idea with unnecessary bells and whistles to me :-)

> Which brings up the point -- is a package abstract (conceptually) a
> separate "compilation unit," which just happens to require that it be
> immediately followed by a corresponding package specification
> compilation unit?  I think that might be the right (conceptual) model,
> though we don't expect them to ever be in physically separate "files"
> (whatever those are ;-).

Actually it seems to me quite natural to have them in separate files, and
I don't know why you say "we don't expect" this. Of course as you say, the
standard has nothing to say about this (as I mentioned earlier, we are
planning to have private parts in separate files at some point, this is
quite useful for many reasons).

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

From: Tucker Taft
Sent: Thursday, June 14, 2001, 8:00 AM

> I really dislike the idea of nested package abstracts, a typical examle
> of cluttering an idea with unnecessary bells and whistles to me :-)

I am not sure either way.  If we don't allow nested package abstracts,
including local ones, we are creating the first clear situation where
you must make a set of packages into library units to create a particular
structure.  In essentially all cases currently, you can take a bunch
of library packages and turn them into library-level subpackages,
and everything still works.

I suppose the simplest counter argument is "so what"?

I guess the reason this is an issue is that one wants the language
to be "recursive" in some sense, that any useful interdependent structure
of library units can be gathered together and created within
some enclosing package, or perhaps more importantly, within
some enclosing generic package for multiple instantiation.

I could go either way...

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

From: Robert Dewar
Sent: Thursday, June 14, 2001, 8:00 AM

The main argument in favor of this feature at all (at least the market
argument as opposed to "this would be nice to have") is to mimic C++ and
Java existing code. So to me the only strong argument for nested package
abstracts would be an argument that they are useful from this point of view.
I do not see that this is the case, but am not really familiar enough to say.

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

From: Ed Schonberg
Sent: Thursday, June 14, 2001, 8:52 AM

Tuck writes:
>  And there is no special "forward" reference.  You would have to split
> them up like this:
>
> procedure Test is
>     abstract
>         type T1;
>         type T1_Ptr is access all T1;
>     package local1;
>
>     package local2 is
>         type rec2 is record
>             a : local1.T1_ptr;
>         end record;
>     end local2;
>
>     package local1 is
>         type T1 is private;

If the abstract can be separated in this fashion, it seems unavoidable that
the abstract can be a standalone compilation unit (you don't want this split
to be allowed only for nested packages, right?). Maybe this is another
argument against local abstracts? I agree with Robert that this seems to
be a case where orthogonality just adds complexity. The original intent of
this feature is just to solve the mutually recursive types problem. This
requires not much more than was available in the "with type" proposal,
i.e. some minimal naming of incomplete types and access types.  Now the
proposal allows constant declarations, subtype declarations with constant
expressions, etc. and it's hard to see why this is needed to solve the original
problem.  Everybody comments on the C++ complications relating to the
three visibility levels (public, private, protected). We are now proposing to
have four different declarative parts for a package: an abstract, a visible
part, a private part, and a body!  I'm afraid that the
immediate reaction will be that once again Ada breaks the complexity ceiling.

The advantage of viewing the abstract as a representation item (sorry if
this upsets your digestion again :-))  is that it can be described as an
annotation on a package, not as another piece of it. For convenience, this
annotation can be introduced before the package declaration is placed in
the environment, but it doesn't have to. It only applies to compilation
units (which is all you need to mimic the Java libraries, still the main
motivation for mutually recursive type declarations). This seems as easy
to implement as the other proposals, and it can be presented as a light
mechanism to solve a specific problem, not as a swiss army knife to be
used only by specially trained professionals!

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

From: Jean-Pierre Rosen
Sent: Thursday, June 14, 2001, 9:42 AM

Just wondering if the notion of package abstract could not provide the equivalent of Java interfaces (following the idea of Robert
that abstracts are here to mimic C++/Java).
The notion looks quite similar: a set of promised features to be implemented by the package...

Let's see: assuming that an abstract is a separate unit, this would imply that *several* packages would be allowed to implement the
*same* abstract.
Or maybe we could have a generic abstract...

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

From: Tucker Taft
Sent: Thursday, June 14, 2001, 5:05 PM

Gack.  We have proposed a separate feature for interfaces.
Interfaces are types in Java.  An interface that is
only a module would be much less useful, and wouldn't
have anywhere near the same functionality as a Java
interface, which has *instances*.

Groan.  Interfaces are instantiatiable at run-time
in Java, and instances can be passed around.  A generic package (abstract)
is definitely not a good equivalent.

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

From: Randy Brukardt
Sent: Thursday, June 14, 2001, 6:41 PM

Yuck! The primary advantage of an "interface" is the ability to dispatch on it.
I really don't think we want to introduce Package'Class!

Have you looked at Tucker's Interface proposal (AI-251)?

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

From: Randy Brukardt
Sent: Thursday, June 14, 2001, 7:15 PM

> If the abstract can be separated in this fashion, it seems unavoidable that
> the abstract can be a standalone compilation unit (you don't want this split
> to be allowed only for nested packages, right?). Maybe this is another
> argument against local abstracts?

Absolutely, that was my first thought. But keep in mind that "local abstracts"
and "nested abstracts" (as proposed by Pascal in the AI) are different things,
and they ought not necessarily be treated together.

> Now the proposal allows constant declarations, subtype declarations
> with constant expressions, etc. and it's hard to see why this is needed to
> solve the original problem.

We have to be able to give representation items in the abstract, in order to
solve the problems with "with type". That pretty much requires allowing
constant declarations. (I don't think subtypes are useful, and indeed they
are not in the list of allowed declarations in the current proposal. Its
best not to set up straw-man arguments -- the reason for carefully
writing these proposals is to make it clear what's proposed.)

> Everybody comments on the C++ complications relating to the
> three visibility levels (public, private, protected). We are now proposing to
> have four different declarative parts for a package: an abstract, a visible
> part, a private part, and a body!  I'm afraid that the
> immediate reaction will be that once again Ada breaks the complexity ceiling.

Good, Ada should always be ahead. :-)

> The advantage of viewing the abstract as a representation item (sorry if
> this upsets your digestion again :-))  is that it can be described as an
> annotation on a package, not as another piece of it.

The annotation idea isn't necessarily bad. But please resist calling this a
representation item, since it have nothing to do with representation. We
spent a lot of time during the Corrigendum work fixing all of the problems
that had been introduced by making non-representation stuff representation
items. Ada 95a :-) does not have representation items that control
non-representation aspects.

One might be able to describe this as an operational item, but that also
seems like a stretch. I think we would end up with the same problems that
we had with the stream attributes. So I think this should be described as
a separate kind of thing.

> For convenience, this annotation can be introduced before the package
> declaration is placed in the environment, but it doesn't have to. It only
> applies to compilation units (which is all you need to mimic the Java
> libraries, still the main motivation for mutually recursive type
> declarations). This seems as easy to implement as the other proposals,
> and it can be presented as a light mechanism to solve a specific problem,
> not as a swiss army knife to be used only by specially trained professionals!

Here is the crux of the issue. Ada 83 and Ada 95 used as their design
philosophy that the language would provide swiss army knifes, not specific
solutions. Thus, we have protected types rather then semaphores.

Has this philosophy been changed? If it has, then the ARG is probably
approaching many of these amendments the wrong way.

One of the problems with adopting vendor solutions is that they tend to be
narrowly focused. That does not fit in with the philosophy of Ada's language
design, and thus we get the sort of conflicts seen here.

I think one of the major benefits of Ada is its modular design, without a
lot of kludgy solutions. I would not want to abandon that design for a bunch
of hacked solutions to very specific (and sometimes dubious) problems.

We have to be careful about "presenting light solutions to solve a specific
problem", because that can backfire. For instance, if this is presented as
a solution to allow interfacing to Java, many Ada users will say
who cares"? It won't help their problems. On the other hand, if it is
presented as a way to break up giant specifications (and, oh, by the way,
it also helps in interfacing to Java), I think it will be much easier to
sell.

Of course, we don't necessarily want to adopt grandiose solutions to these
problems (any more than we did with Ada 95 -- compare controlled types to
the original user-defined assignment proposal). But I don't think very
narrow solutions are good unless broader solutions are explored and
rejected. (Which is my objection to AI-260 -- the solution is fine, but
it doesn't solve the root problem which comes up in other cases as well:
we need to look at addressing that. And if THAT fails, then adopt a narrow
solution.)

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

From: Steve Baird
Sent: Thursday, June 14, 2001, 4:16 PM

Tuck says:
  Wouldn't it be better to make a "with_abstract" override an inherited
  "with_unit" clause?  That way if the inherited "with_unit" clause is
  later removed, you wouldn't get any nasty surprises.

I don't like this "flickering visibility" proposal.

Once full visibility has been turned on, there is no
benefit (with respect to resolving circularities)
in turning it off again.

You could get surprises the other way too (e.g.
a valid subprogram declaration which cannot be completed
because visibility to some entity mentioned in the
initial declaration has been lost).

Consider

     abstract
         type T;
         type Ref is access T;
     package P is
         function "=" (L, R : Ref) return Boolean;
         type T is limited private;
     private
         ...
     end P;

Resolutions of P."=" are probably going to be confusing no
matter what rule you choose, but the flickering rule seems
to me to be particularly treacherous. Once an operator has
been hidden, it should stay hidden.

----

Regarding visibility within an abstract, Tuck says:

  As mentioned in a separate note, Standard obviously needs to be
  a special case.  Perhaps all of Standard is in its abstract part,
  conceptually.

Perhaps the special case should be Pure units, not Standard.

Something like:
    A package abstract has visibility into the abstracts
    of all ancestor units.
    A package abstract has visibility into all pure ancestor units.

Would this work?
Even if it would work, are purity-based visibility rules a good idea?

----

Tuck says:
   I don't think we want *any* "with" clauses to apply to package abstracts.

Pascal gives an example in which it is useful for a package abstract
to be able to refer to a constant declared in System.

Again, a purity-based rule might work.

Something like:
    An abstract can see a withed unit only if the unit is Pure.

However, care must be taken to avoid introducing cyclic dependencies.

One way to resolve this would be to disallow abstracts for Pure units
(this would be no big deal if it is true that units with abstracts almost
always declare named access types).

----

How does "partial freezing" (i.e. "all aspects of its representation except
for its storage pool") really work? This notion that a type is
frozen enough to allow objects to be declared, but still not
completely frozen is something new (see 13.14(19)).

What freezing rule prevents an allocator from preceding a storage pool rep
spec, as in

     with Q;
     abstract
         type T is access Integer;
     package P is
         Foo_Flag : Boolean := Q.Foo (new Integer); -- legal?
         Bar_Flag : Boolean := Q.Bar; -- legal?
         for T'Storage_Pool use ... ;
     end P;

     with abstract P;
     package Q is
         function Foo (X : P.T) return Boolean;
         function Bar (X : P.T := new Integer) return Boolean; -- legal?
     end Q;

?

----

Tuck says:
     Perhaps you mean that an allocator for an access type is only legal if
     the enclosing package's specification is visible.

This rule makes me nervous - I'm concerned about allocators in units which
have only an indirect semantic dependence on the unit declaring the
access type. How is this allocator legality rule defined in this case?

Would adding a rule that the designated type of an access type
declared in a package abstract must be an incomplete type (i.e. incomplete
at the point of the access type declaration) solve this problem
and eliminate the need for any special allocator legality rules
(by leveraging the existing restrictions on the use of incomplete types)?

Is there a "bad" allocator that this rule would allow?

This might also allow elimination of the rule prohibiting passing an access
type declared in a package abstract as an actual for a formal access type.

The access type could also be defined to be "fully frozen" (i.e. now it
is too late to supply a storage_pool rep spec) at the freezing
point of the designated type. At this point in the execution, the collection
for the access type could be created (for implementations which do
this sort of thing).

----

It seems like the rules for package renames need to be spelled out. Again,
there are problems with determining how much of a package is visible
in the case where the semantic dependence on the package is only indirect.

   abstract
   package A is
       X : Integer;
   end A;

   with abstract A;
   package B is
        package A_Rename renames A;
   end B;

   abstract
   package A.Child is
   end A.Child;

   with A.Child;
   with B;
   package C is
        Y : Integer renames B.A_Rename.X; -- legal ?
        package Child renames B.A_Rename.Child; -- legal ?
   end C;

   -- Variation: replace "with A.Child;" with "with abstract A.Child;"

One might consider using the P'Abstract syntax in a package rename, as in
     package P_Abstract renames P'Abstract;
and disallowing a "normal" rename of a package whose full-spec is
not visible. This would clarify which view of the package is
being renamed. Alternatively, one might simply disallow renames of
packages whose full specs are not visible.

----

There are similar problems with "use type" in the case where the
semantic dependence on the enclosing package of the type is only indirect.

     abstract
         type T;
         type Ref is access T;
     package P is
         X : Ref;
         function "=" (L, R : Ref) return Boolean;
         function Bar (X : Ref) return Boolean;
         type T is ...;
     end P;

     with abstract P;
     package Q1 is
         subtype S is P.Ref;
     end Q1;

     with P;
     package Q2 is
         subtype S is P.Ref;
     end Q2;

     with Q1;
     with Q2;
     procedure Foo is
     begin
         declare
           X : Q1.S
           use type Q1.S;
           use type Q2.S;
           Flag1 : Boolean := (X = X); -- legal? if so, which "=" op ?
           Flag2 : Boolean := Bar (X); -- legal?
         begin
            null;
         end;

         -- Variation #1: comment out 1st use-type
         -- Variation #2: comment out 2nd use-type
         -- Variation #3: comment out 2nd use-type and with of Q2
      end;

----

Pascal points out that streaming attributes for access types
declared in package abstracts may also be problematic. It seems that
an access type declared in an abstract cannot have user-specified
streaming operations due to the freezing rules. Defining the freezing
rules to allow something like

    with ...;
    abstract
        type T;
        type Ref is access T;
    package P is
        function Inp (...) return Ref;
        for Ref'Input use Inp;
        type T is ... ;
    end P;

would introduce problems of its own, with clients of the abstract
being able to perform streaming operations while unaware of the
attribute_definition_clause (and before it has been elaborated).

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

From: Tucker Taft
Sent: Thursday, June 14, 2001, 5:59 PM

"Baird, Steve" wrote:
>
> Tuck says:
>   Wouldn't it be better to make a "with_abstract" override an inherited
>   "with_unit" clause?  That way if the inherited "with_unit" clause is
>   later removed, you wouldn't get any nasty surprises.
>
> I don't like this "flickering visibility" proposal.
>
> Once full visibility has been turned on, there is no
> benefit (with respect to resolving circularities)
> in turning it off again.

I would claim there is some from a maintenance point of
view, in that you know that a given library package does
not depend on things outside of the abstract.
If you don't like flickering visibility, then I would
say we should make with P'Abstract illegal if P is already
"fully" visible due to an inherited "with" clause.

>
> You could get surprises the other way too (e.g.
> a valid subprogram declaration which cannot be completed
> because visibility to some entity mentioned in the
> initial declaration has been lost).

I don't understand this one.  The subprogram declaration
and its completion need to be in the same library unit.
I am only talking about inheritance from ancestors.
I would say that the body must not "with P'abstract" if
the spec has a "with P".  That would certainly create
confusion.

>
> Consider
>
>      abstract
>          type T;
>          type Ref is access T;
>      package P is
>          function "=" (L, R : Ref) return Boolean;
>          type T is limited private;
>      private
>          ...
>      end P;
>
> Resolutions of P."=" are probably going to be confusing no
> matter what rule you choose, but the flickering rule seems
> to me to be particularly treacherous. Once an operator has
> been hidden, it should stay hidden.

I don't follow this example.  How does this relate to
whether "with P'Abstract" on a child unit overrides a
"with P" on an ancestor library unit.
>
> ----
>
> Regarding visibility within an abstract, Tuck says:
>
>   As mentioned in a separate note, Standard obviously needs to be
>   a special case.  Perhaps all of Standard is in its abstract part,
>   conceptually.
>
> Perhaps the special case should be Pure units, not Standard.
>
> Something like:
>     A package abstract has visibility into the abstracts
>     of all ancestor units.
>     A package abstract has visibility into all pure ancestor units.
>
> Would this work?
> Even if it would work, are purity-based visibility rules a good idea?

Purity seems a bit of a lousy basis for distinguishing
what is visible, though I agree that if we allow
an abstract to see a "full" spec, that spec must
be pure.

Perhaps we should go back to making an abstract
a separate compilation unit, but allow implementations
to require that an abstract and the spec be contiguous
in the same compilation.  Rational and others would
choose to enforce this optional requirement.

Once it is a separate compilation unit, it becomes
obvious which "with" clauses apply to the abstract,
and which apply to the spec.

>
> ...
> How does "partial freezing" (i.e. "all aspects of its representation except
> for its storage pool") really work? This notion that a type is
> frozen enough to allow objects to be declared, but still not
> completely frozen is something new (see 13.14(19)).

We can break the type into separate aspects, one
being the storage pool aspect, and the other being
the access value representation aspect.

>
> What freezing rule prevents an allocator from preceding a storage pool rep
> spec, as in
>
>      with Q;
>      abstract
>          type T is access Integer;
>      package P is
>          Foo_Flag : Boolean := Q.Foo (new Integer); -- legal?
>          Bar_Flag : Boolean := Q.Bar; -- legal?

These are both legal.  But they freeze the storage pool
aspect of the access type, so...

>          for T'Storage_Pool use ... ;

This is illegal, since the storage pool aspect has already
been frozen.

>      end P;
>
>      with abstract P;
>      package Q is
>          function Foo (X : P.T) return Boolean;
>          function Bar (X : P.T := new Integer) return Boolean; -- legal?

Default expressions don't do freezing where declared --
they freeze where they are used, so perhaps this could
be allowed.

>      end Q;
>
> ?
>
> ----
>
> Tuck says:
>      Perhaps you mean that an allocator for an access type is only legal if
>      the enclosing package's specification is visible.
>
> This rule makes me nervous - I'm concerned about allocators in units which
> have only an indirect semantic dependence on the unit declaring the
> access type. How is this allocator legality rule defined in this case?

I didn't really mean "visible."  What I should have
said is, approximately, "in scope."   Or equivalently,
it must semantically depend on the enclosing package spec.
To preserve my goal of having a "with P'Abstract" override
with P, we could invert the rule to say that you cannot
use the allocator if your are within the scope of
a "with P'Abstract," and say that the scope of a with
clause does not include nested scopes that "with" the
same unit (potentially with different "abstractness.").


> Would adding a rule that the designated type of an access type
> declared in a package abstract must be an incomplete type (i.e. incomplete
> at the point of the access type declaration) solve this problem
> and eliminate the need for any special allocator legality rules
> (by leveraging the existing restrictions on the use of incomplete types)?

I think we will still get into trouble in terms of
resolving direct vs. indirect dependencies.

>
> Is there a "bad" allocator that this rule would allow?
>
> This might also allow elimination of the rule prohibiting passing an access
> type declared in a package abstract as an actual for a formal access type.
>
> The access type could also be defined to be "fully frozen" (i.e. now it
> is too late to supply a storage_pool rep spec) at the freezing
> point of the designated type. At this point in the execution, the collection
> for the access type could be created (for implementations which do
> this sort of thing).

That seems a bit awkward.  Why not just make an allocator
freeze the type completely?

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

From: Ed Schonberg
Sent: Friday, June 15, 2001, 4:23 PM

>  Good, Ada should always be ahead. :-)

My goal as well... However, competing on complexity seems like the wrong
way to go :-)!

>  The annotation idea isn't necessarily bad. But please resist calling this a
>  representation item, since it have nothing to do with representation. We
>  spent a lot of time during the Corrigendum work fixing all of the problems
>  that had been introduced by making non-representation stuff representation
>  items. Ada 95a :-) does not have representation items that control
>  non-representation aspects.

I'm perfectly happy with another name, I just find the syntax suggestive,
for something that is an annotation to an existing (or future) declaration.

>  Here is the crux of the issue. Ada 83 and Ada 95 used as their design
>  philosophy
>  that the language would provide swiss army knifes, not specific solutions.
>  Thus,  we have protected types rather then semaphores.

>  Has this philosophy been changed? If it has, then the ARG is probably
>  approaching  many of these amendments the wrong way.

No objection to this statement of principle. What I notice concerning this
feature is that the original problem was indeed circumscribed, but the
solution has grown in complexity without its motivation having changed in
the least. Seems like a disconnect. The visibility problems that are being
teased out are no doubt just the beginning, when we start implementing this
we will come across others.  Ada already has a good set of swiss army
knives, I'm not convinced that we need an additional one of the proposed size.
For example, the use of with-clauses seems problematic (Standard only?
pure packages only?) and if all we need is constants, why not exclude with-
clauses altogether, and have only named numbers? What problem is being
solved by having named entities imported from elsewhere?

>  One of the problems with adopting vendor solutions is that they tend to be
>  narrowly focused. That does not fit in with the philosophy of Ada's language
>  design, and thus we get the sort of conflicts seen here.

I'm not advocating any particular vendor solution (with_type was an ARG
proposal, remember?). I just notice that the complexity of the new solution
bears no relationship to the complexity (or the urgency) of the problem.

>  We have to be careful about "presenting light solutions to solve a specific
>  problem", because that can backfire. For instance, if this is presented as a
>  solution to allow interfacing to Java, many Ada users will say "who cares"?
>  It won't help their problems. On the other hand, if it is presented as a way
>  to break up giant specifications (and, oh, by the way, it also helps in
>  interfacing to Java), I think it will be much easier to sell.

And if it is presented as a light mechanism that is easy to describe it has
an even better chance. The difficulties that Steve outlined will require about
two pages of RM rules to describe the semantics of abstracts. I don't think
this will sell at all.  The with_type proposal required one paragraph.
The "with P'Abstract" and "for P'Abtract use.." is equally simple. No nesting,
no with_clauses, no modification of the production for package declarations,
no confusion as to the respective roles of three different declarative
parts.

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

From: Ed Schonberg
Sent: Friday, June 15, 2001, 5:01 PM

> No objection to this statement of principle. What I notice concerning this
> feature is that the original problem was indeed circumscribed, but the
> solution has grown in complexity without its motivation having changed in
> the least. Seems like a disconnect.

Fair enough, but I don't agree. "with type" requires another amendment about
implicit conversions of access types (because its not possible in general to
allow with type on access types), and that has to be included in the complexity
rating so we have a fair comparison.

> The visibility problems that are being teased out are no doubt just the
> beginning, when we start implementing this we will come across others.

Of course. That is why Tucker proposed abandoning "with type", because of the
problems he uncovered implementing it.

>  Ada already has a good set of swiss army knives, I'm not convinced that
> we need an additional one of the proposed size.
> For example, the use of with-clauses seems problematic (Standard only?
> pure packages only?) and if all we need is constants, why not exclude with-
> clauses altogether, and have only named numbers? What problem is being
> solved by having named entities imported from elsewhere?

Good question. I don't know. But it does seem likely that you might need to
import a value from elsewhere. For instance, our compiler has a package Host
that describes the Host system. A representation clause for an access type
would want to use the constants in that package to get the appropriate size.

(It is the inability to describe the representation of the access type that is
the real sigificant problem with "with type".)

> And if it is presented as a light mechanism that is easy to describe it has
> an even better chance. The difficulties that Steve outlined will require about
> two pages of RM rules to describe the semantics of abstracts. I don't think
> this will sell at all.  The with_type proposal required one paragraph.

Correction: it is at least three, probably more to deal with the "conflict"
problems noted by Tuck.

> The "with P'Abstract" and "for P'Abtract use.." is equally simple. No nesting,
> no with_clauses, no modification of the production for package declarations,
> no confusion as to the respective roles of three different declarative
> parts.

I don't see this at all. To me, it looks like alternative syntax with the same
set of problems. Perhaps you could write up a full proposal for this (similar
to the level that Pascal did with the previous one), so we can see how simple
it is??

Personally, I suspect that this is simply a hard problem that is going to defy
an elegant solution. The collision problems just don't seem to be going away,
we just move them from place to place. The access problems aren't going away,
THEY just get moved around. If this issue wasn't so important, I'd suggest
scrapping the whole thing.

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

From: Robert Duff
Sent: Friday, June 15, 2001, 5:59 PM

I have finally read this giant thread (90 printed pages or so!),
and I have some comments about the semantics, which I'll give later.

But as Robert said, it's easier to worry about surface syntax:

I like Ed's "with P'Abstract;" proposal.  It solves the readability
concerns of the earlier proposals.  I scoff at the idea that it's hard
to parse: come on, you guys, syntax changes are the *easy* part!
This isn't the first time we've all had to make our parser accept a
reserved word as an attribute.

I do not like "for P'Abstract use ...".  Partly because the current
syntax for a "for ..." requires a name or expression (not a
package-thingy-tail).  And partly because I just don't like it.  ;-)

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

From: Steve Baird
Sent: Friday, June 15, 2001, 8:35 PM

> If you don't like flickering visibility, then I would
> say we should make with P'Abstract illegal if P is already
> "fully" visible due to an inherited "with" clause.

Or define the redundant with of P'Abstract to have no effect.
Either is ok by me.

----

> >      abstract
> >          type T;
> >          type Ref is access T;
> >      package P is
> >          function "=" (L, R : Ref) return Boolean;
> >          type T is limited private;
> >      private
> >          ...
> >      end P;

> I don't follow this example.  How does this relate to
> whether "with P'Abstract" on a child unit overrides a
> "with P" on an ancestor library unit.

When I wrote that example, I thought we were also talking
about overriding in the case of a a spec withing P and the body withing
P'Abstract; I was confused.

Still, to answer your question ...

with P;
package Parent is
    function "=" (L, R : P.T) return Boolean renames P."=";
end Parent;

with P'Abstract;
package Parent.Child is
    function "=" (L, R : P.T) return Boolean renames P."=";
end;

It would seem odd (at least to me) if these two uses of P."=";
would resolve to different things. Going from a parent unit to a child unit,

I don't expect to get reemergence of hidden operators.

Other people may have different expectations.

Not a big deal.

----

> Purity seems a bit of a lousy basis for distinguishing
> what is visible, though I agree that if we allow
> an abstract to see a "full" spec, that spec must
> be pure.

I don't much like it myself; I think it does solve
some problems and might serve as food for thought.

> Perhaps we should go back to making an abstract
> a separate compilation unit, ...

> Once it is a separate compilation unit, it becomes
> obvious which "with" clauses apply to the abstract,
> and which apply to the spec.

True. Or we could generalize AI-00262's mechanism with another
with_clause modifier. But this could lead to oddness such as

    abstract with P'Abstract;

, meaning that P'Abstract is to be visible in the abstract
of the withing unit. Maybe this is ok ...

----

> I didn't really mean "visible."  What I should have
> said is, approximately, "in scope."  Or equivalently,
> it must semantically depend on the enclosing package spec.

It sounds like you are suggesting a rule that allocators
are legal for an access type declared in a package abstract
if the unit containing the allocator semantically depends
on the full package spec.

This means that deleting an otherwise unused with from
a distant unit (e.g. an empty package spec) can affect the
legality of a unit, which seems bad:

    abstract
        type T is access Integer;
    package P is
    end P;

    with P; package Q1 is end Q1;
    with Q1; package Q2 is end Q2;
    ...
    with Q8; package Q9 is end Q9;

    with Q9;
    with P'Abstract;
    package Client is
         X : P.T := new Integer;
    end Client;

Deleting Q1's with of P causes Client's allocator to become
illegal. I think that users would find this confusing and it
is certainly not a good thing from the point of view of incremental
compilation.

> > Would adding a rule that the designated type of an access type
> > declared in a package abstract must be an incomplete type (i.e.
 incomplete
> > at the point of the access type declaration) solve this problem
> > and eliminate the need for any special allocator legality rules
> > (by leveraging the existing restrictions on the use of incomplete
 types)?

> I think we will still get into trouble in terms of
> resolving direct vs. indirect dependencies.

Fine, scratch this idea for now. I think I need a clearer understanding
of the rules for determining whether an incomplete type declared in an
abstract is considered to be complete when viewed from outside
the declaring unit.

Is the rule simply "the type is complete if there exists a semantic
dependence on the full package spec"?

    abstract
        type T;
        type Ref is access P.T;
    package P is
        type T is new Integer;
    end P;

    with P'Abstract;
    package Q is
         Ptr : P.Ref;
    end Q;

    with P, Q;
    package R1 is
        X : Integer := Integer (Q.Ptr.all) -- legal ?
    end R1;

    with P; package Q1 is end Q1;
    with Q1; package Q2 is end Q2;
    ...
    with Q8; package Q9 is end Q9;

    with Q9, Q;
    package R2 is
        X : Integer := Integer (Q.Ptr.all); -- legal ?
    end R2;

    with Q;
    package R3 is
        X : Integer := Integer (Q.Ptr.all); -- certainly illegal
    end R3;

If so, then this has the same undesirable "dependence on distant withs"
property as was mentioned earlier.

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

From: Robert Duff
Sent: Saturday, June 16, 2001, 4:24 PM

> >  The annotation idea isn't necessarily bad. But please resist calling this
> >  a representation item, since it have nothing to do with representation. We
> >  spent a lot of time during the Corrigendum work fixing all of the problems
> >  that had been introduced by making non-representation stuff representation
> >  items. Ada 95a :-) does not have representation items that control
> >  non-representation aspects.
>
> I'm perfectly happy with another name, I just find the syntax suggestive,
> for something that is an annotation to an existing (or future) declaration.

But this is already true of existing package "parts": there's only one
"package", but the "visible part of it" gives a certain view of it, and
the private part and body also.  Adding a "package abstract" is just
another "part" of the definition of the one and only "package".  We
don't need rep-clause-like syntax to say that -- we already have it with
the current "parts" of a package.

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

From: Robert Duff
Sent: Saturday, June 16, 2001, 3:52 PM

> > >      abstract
> > >          type T;
> > >          type Ref is access T;
> > >      package P is
> > >          function "=" (L, R : Ref) return Boolean;
> > >          type T is limited private;
> > >      private
> > >          ...
> > >      end P;

> with P;
> package Parent is
>     function "=" (L, R : P.T) return Boolean renames P."=";
> end Parent;
>
> with P'Abstract;
> package Parent.Child is
>     function "=" (L, R : P.T) return Boolean renames P."=";
> end;
>
> It would seem odd (at least to me) if these two uses of P."=";
> would resolve to different things. Going from a parent unit to a child unit,

The same oddity exists when declaring "=" in a private part.
To me, the *really* odd thing here, is that you can't do anything about
it.  You can't declare "=" in the abstract, thus making sure everybody
has the same "=".  Why not?  Or is the correct answer, "Who cares?"?

> > Purity seems a bit of a lousy basis for distinguishing
> > what is visible, though I agree that if we allow
> > an abstract to see a "full" spec, that spec must
> > be pure.
>
> I don't much like it myself; I think it does solve
> some problems and might serve as food for thought.

Yes, food for thought.

But I really don't like any visibility rules to depend on purity (ie
pragma-Pure-ness).  I find myself changing pragmas Pure and Preelaborate
and so forth all the time, just to satisfy silly language rules (because
pragma Pure doesn't correspond to any sensible definition of purity).
And these changes annoyingly cascade through the source code.
Basing visibility rules on that would be like building your house on
shifting sands. (Yeah, Steve, I know your house is on an earthquake fault,
but that's beside the point.  ;-))

>... I think I need a clearer understanding
> of the rules for determining whether an incomplete type declared in an
> abstract is considered to be complete when viewed from outside
> the declaring unit.

Me, too.

P.S. I think Tuck calls those distant dependencies the "Ripple Effect".
Not to be confused with the "Beaujolais effect", given that we're naming
"effects" after "popular wines".  Or is that something else, Tuck?

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

From: Robert Duff
Sent: Saturday, June 16, 2001, 4:18 PM

OK, here are my comments on AI-271.

First: there is too little rationale in the discussion section of the
AI.  I see a whole bunch of rules and regulations, but I'm having a hard
time understanding why they're there.  I suppose I would understand
better if I had come to the last ARG meeting.  Sorry about that: I had
planned to come, but I couldn't because of family reasons.  I'll try to
get to the next one.  But in any case, the AI ought to explain the
answers to the various questions I have below.  (No, I'm not
volunteering to write it up -- especially since I don't know the
answers.)

We should make clear that "abstract" is a noun, here, whereas elsewhere
in the language it's a verb.  It reminds me of the many meanings of
"virtual" in C++.  Sigh; I guess it's OK.

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

I like Tuck's "package abstract" proposal better than "with type",
because a package abstract is a concrete "thing" you can get your hands
on.  In fact, it's a "compilation unit" -- a piece of separately
compilable text in my source code, and a separate "thing" the compiler
deals with as a unit.

(Of course, these esoteric reasons are not sufficient to reject "with
type", given that GNAT has already implemented something.  We apparently
rejected "with type" because it has fundamental flaws, described
elsewhere.)

Currently, a package has three parts: visible part, private part, and
body.  The visible and private parts are syntactically combined, because
the compiler needs to see them both at once, in order to generate
reasonably efficient code (and probably in order to check legality in
some rare cases, despite Pascal's valiant attempts to keep private parts
private).  More precisely, when compiling a comp_unit that says
"with P;" the compiler needs to look at both the visible and private
parts of P.

Tuck's proposal is to add another part, so we have four: abstract,
visible part, private part, and body.  In terms of syntax, these form
*three* compilation units: abstract, spec, and body.)

(The GNAT folks have been threatening for years to allow the private
part to be in a separate file.  That's a good idea, but the compiler
will *still* look at both parts when compiling dependents of the
package.  That's "merely" a source representation issue -- the "spec" is
still a single comp_unit.  So this has no bearing on whether the
abstract should be a separate comp_unit.)

Pascal proposes to combine the abstract syntactically with the spec.
I don't like that.  It muddies the waters: is the abstract a comp_unit
or not?  Well, textually, it is not.  But the compiler is expected to
compile the same comp_unit (text) twice, in two different modes, to
produce two different comp_units internally.  That seems kludgy, to me.
From the user's point of view, the source text no longer reflects what's
going on in the compiler.  If you have one (textual) comp_unit per file
(which is usual), you can no longer tell which (internal) comp_units
have changed by looking at time stamps.  You can't "configuration
manage" the abstract itself.

The abstract really *is* a separate comp_unit, because (we all agree)
there are separate semantic dependences upon it.  For a non-incremental
compiler (source-based or library-based), if X depends on the abstract,
then X must be recompiled if the abstract changes, but not if the
visible or private part changes.

If the "compile twice in different modes" method makes Pascal's life
easier, then we should make some rules that allow that method, but we
should not infect the basic syntax or semantics with that method.  Eg,
Tucker recently suggested a rule saying the compiler can require that
the two be placed together in the same source file (similar to the
current 10.1(4)).  That's OK with me, but I don't think we should
require that all compilers do it that way: it makes perfect sense to
compile the abstract separately, because the things that depend on the
abstract do not depend on the visible and private parts (ie the spec).

The rationale for Pascal's proposal seems to be to avoid changing all
the peripheral tools and whatnot, as opposed to the compiler itself.
Eg, if abstracts are comp_units, one would have to invent a new
file-naming convention for abstracts, and the recompilation tool would
have to know about them.  I don't buy it: it seems to me that the
recompilation tool would be *simpler* if textual comp_units match
internal comp_units, rather than having to gin something up that's not
really there.  Either way, the recompilation tool needs to know about
the abstract as a separate entity, and will definitely need to be
changed.

But as I said, if it makes Pascal's (and others') life easier, I'm happy
to allow that model.  I just don't want it to pollute the RM with that
kind of wording.

I think Pascal's syntax raises some issues better left alone.  In
"abstract-only mode", the compiler is going to "process" the package
spec, but quit when it's done with the abstract.  But does the compiler
parse the stuff after the abstract?  Must it/may it?  That is, can the
compiler reject the abstract if there's a syntax error after the
abstract?  Can it reject the abstract if there's a semantic error?
May I compile:

    abstract
        type T;
    package <insert contents of local newspaper here>

in abstract-only mode?  I guess the answer must be, "Implementation
Defined".  (Note that the RM makes no distinction between (context free)
syntax vs semantic errors, given that the RM syntax is not context free,
and is resolved partly by overload resolution.)

And the compiler is presumably required to do some sort of
difference-analysis on the text of the package, to see whether the
abstract changed.

I really think we need to admit that the abstract is a separate
compilation unit.  Pascal's proposal makes that muddy (how can there be
a separate elaboration order for the two things if they're just one
thing?).  Better to have separate text for separate things.

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

The goal of this feature is to allow mutual recursion among separately
compiled types (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 other restrictions on the
programmer.  (After all, you can *already* have such mutual recursion,
if you're willing to make everything tagged, and sprinkle bogus type
conversions all over.  We're trying to allow mutual recursion without
those burdens.)
Have I at least got the goal right?

I would like to hear some rationale for what is allowed in an abstract.
What is the minimal set of declarations that meet the goal, and what
additional declarations are harmless?

As to the minimal set: We need incomplete [tagged] types, and access-all
to those.  We need certain rep clauses/pragmas on those access types (in
particular, 'Size, 'Alignment, and impl-def ones -- any others?).  To
support those rep clauses, we need static constants.

I can imagine a few other things that might be nice.  Eg, I might want
to say "for Acc_Type'Size use Integer'Size;".  (Or My_Integer'Size.)
A use_clause might be convenient.

The current proposal allows access-to-T where T is *not* incomplete.  Is
that desirable, or it just easier to write the rules that way?  Does it
cause trouble?

What's the maximal set?  Or what are the principles that say we can't
allow certain things?

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

A related issue is what other comp_units (or contents thereof) should be
visible in an abstract.

It seems odd to me that the things you can declare *in* an abstract are
different from the things you can *see from* an abstract.  That is, if
this is legal:

    package abstract P is
        type T is tagged;
        type A is access all T'Class;
        for A'Size use Integer'Size * 2;
    end P;

(where we've referred to Integer and "*" from our parent), then why
shouldn't this also be legal:

    package abstract P is
        type My_Integer is range 1..10;
        type T is tagged;
        type A is access all T'Class;
        for A'Size use My_Integer'Size * 2;
    end P;

(where we're referring to a local My_Integer and its "*" operator?).

Can we simplify things by making the rules about what an abstract can
see match what an abstract can contain?

Tucker has suggested that package Standard be a special case.  I don't
like that: the rules for Standard should follow from the general rules.
I don't like any rule that says the contents of Standard are part of its
abstract, unless those declarations are generally legal in an abstract.
Tucker has also suggested that an abstract could see certain things from
its ancestors, but not from with-ed packages.  I don't like that either
-- it seems to me that if you can see a constant decl, it shouldn't
matter whether it's from your parent or from a with-ed package.
Similarly, if you can say Integer'Size (from Standard), you ought to be
able to say Some_Package.My_Integer'Size (given "with Some_Package;" or
"with Some_Package'Abstract"?).  Somebody gave an example where it was
useful to import something from System for a 'Size clause on an access
type.

Some have suggested that abstracts can see (only) pragma-Pure packages.
If so, does that mean that anything allowed in a pragma-Pure package
should also be allowed in an abstract?

Alternatively, why not let an abstract import anything it likes?  But
then have rules saying all expressions must be static and whatnot.
But "for A'Size ..." already has to be static, so what's the issue with
staticness?  The current proposal seems a mixture: try to restrict
imports to pragma-Pure packages, and also try to restrict expressions to
static.  Which is it?

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

Elaboration Issues.

It seems that one unstated goal is to make sure that no elaboration code
is generated for an abstract.  True?

If so, why?  The important thing is that the compiler can analyze and
code-gen a client of P'Abstract without looking at the rest of package P.
This means that anything *partially* declared in P'Abstract must have
restrictions: most types are incomplete, so you can only use them in
certain ways, access types don't allow allocators, deferred constants
don't make sense, etc.  But I don't see any harm in allowing arbitrary
code in the abstract, so long as the declarations are fully complete.

If certain restrictions make compilers simpler, I'm all for it.  But I
want to understand why -- I don't like making so-and-so illegal merely
because we haven't thought of any important use for so-and-so.

If abstracts could contain (or depend upon) run-time stuff, then we
could allow "for A'Storage_Pool use ..." in an abstract.  Wouldn't that
simplify the proposed rules (which call for "sort of frozen" access
types)?  We could then say that an access type in an abstract is frozen
at the end of the abstract, no?

If it is indeed true that an abstract should have no elaboration code,
then there are two ways to say that: they don't get elaborated, or they
do get elaborated, but their elaboration has no effect.  The AI takes
the second approach.  Is there a good reason?  Or are these two
equivalent ways of saying the same thing?
Actually, I suppose there's a third way: they do get elaborated, and
just like any other package part, the elaboration consists of
elaborating all the stuff therein, but we have made sure that all that
stuff doesn't do anything interesting at run time.  Is this all just six
of one or half a dozen of another?

I'm inclined to say that an abstract can "with" anything or be a child
of anything.  An abstract B can choose to "with A;" or "with
A'Abstract;".  The visibility rules work as usual, considering an
abstract to come before the vis/priv/body.  But then to add whatever
restrictions are necessary to make sure that the compiler can do "with
X'Abstract;" without looking at the rest of X.  It's already true that
'Size clauses on access types must be static.  And you can't give a
'Size clause for an incomplete type.  And allocators are illegal if the
desig type is incomplete.

By the way, I think a textual description of what's legal in an abstract
would be preferable to the AI's syntactic definition.  Either "the
following declarative_items are allowed in an abstract: static
constants, general access types where the desig type is incomplete,
...", or "the following are not allowed: ...", depending on whether few
things or lots of things are allowed.  Eg, it's not (formally) clear
that the rules for named numbers apply, given that the syntax repeats,
rather than refers to, the syntax for those.

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

Default abstracts.

The original proposal said that the optionality of abstracts was
analogous to the optionality of subprogram specs.  To make that analogy
work, I think we should say that every package has an abstract.  If it's
not explicit, then the abstract is empty.  You can say "with P;" when P
is a procedure body with no spec.  Likewise, you could say "with
abstract X;" when X is a package spec with no abstract.  Does that make
any sense?

Is the current intent that "with X'Abstract;" be illegal if X has no
abstract?  Why [not]?

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

Misc. questions and comments.

Why should the package spec repeat various info from the abstract?
Eg, access type decls, rep clauses.

Why are pool-specific access types not allowed in an abstract?

Why is it illegal to pass an access type in an abstract to a generic
formal access type?
I presume it is OK to pass it to a generic formal private, right?
(I think that's important to allow.)

There is no need for an incomplete type to have discriminants.  The full
type can, and anyway, you can't constrain the incomplete type or do
anything else with the discrims.  Methinks incomplete types with
discrims are just a leftover from Ada 83.

Private packages should be allowed to have abstracts.  I try to make as
many packages as possible private.  I don't want to make a choice here.

An incomplete type in an abstract can be completed by a private type,
which is further completed by a full type.  I think it would simplify
the language if we also allowed an incomplete type in a package
*visible* part to be so completed.  Then the abstract one would not be
"special".

Mutual recursion between parent and child should be supported just as
well as between siblings that 'with' each other.

Why is an abstract_interface_definition allowed in an abstract?
Presumably because an incomplete type cannot be completed by an
abstract_interface_definition.  Should that be changed, to simplify
what's allowed?

There's been some argument as to whether "with P'Abstract;" should
override "with P;" or vice-versa or both or neither.  I think it would
be odd to have a feature that negates previous visibility.  So I think
"with P'Abstract" should be redundant or illegal, given "with P".
(Actually, if I ran the circus, a redundant "with P" would already be
illegal, but I wouldn't dare propose such an upward incompatible
change.)

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

Syntax:

I like Tucker's proposal for package abstracts:

    package abstract P is ... end P;

I like Ed's proposal for importing them:

    with P'Abstract, Q, R'Abstract, S;

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

Three other issues are on my mind, but I haven't anything coherent to
say right now:
Should physically nested package abstracts be legal
(as opposed to children/lib units)?
What is the interaction with generics, if any?
What about renamings of package [abstract]s?

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

From: Robert Duff
Sent: Saturday, June 16, 2001, 4:29 PM

> Absolutely, that was my first thought. But keep in mind that "local
> abstracts" and "nested abstracts" (as proposed by Pascal in the AI) are
> different things, and they ought not necessarily be treated together.

Could you please explain the difference?  I thought "local" or "nested"
abstracts meant the same thing -- abstracts of packages that are
physically nested as opposed to being library units.  They could be used
for two purposes:

1. Mutual recursion among physically nested packages.
2. If you can see the abstract of a given library unit,
you can see the abstracts of packages nested within it.

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

From: Randy Brukardt
Sent: Tuesday, July, 3, 2001, 5:06 PM

No (at least in my meaning), a "local" abstract is one on a package in the
specification, which allows mutual recursion between nested, local packages.
OTOH, a "nested" abstract (as defined by Pascal's proposal) is an abstract
nested in an abstract; it provides an abstract for a nested package.

The question is really whether abstracts are first-class package parts which
are usually used as library units, or whether they are completely special and
used solely as library units (or nested within another abstract).

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

From: Robert Duff
Sent: Saturday, June 16, 2001, 4:40 PM

Pascal says:

> I say "yuck" whenever I see a string of more than two reserved words.  Hence:
>
>     type T is abstract tagged limited private; -- Yuck, yuck, yuck

But you forgot to complain about the more egregious:

    type T is abstract tagged limited null record;

which deserves four "yuck"s by your reckoning.  I think that's the
longest allowed in Ada.

I must say: I don't have any aversion to long series of reserved words.
If it makes sense, I have no objection to "private with abstract Mumble",
and the like.

JDI expressed the same complaint -- maybe it's a French thing.  ;-)

Norm Cohen, on the other hand, could make a sentence out of 50 Ada
reserved words in a row...

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

From: Robert Duff
Sent: Saturday, June 16, 2001, 4:48 PM

Pascal says:

> Hmm, I guess I am not too convinced by this argument.  If two units
> participate in a cycle, it doesn't seem absurd to me that they both have
> abstracts.

It's OK if both have abstracts.  But I suspect that one common case is
where the cycles are of length 2, and one particular one is special.
I think the special one would have an abstract, and many others would
hang off of that.

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

From: Robert Duff
Sent: Saturday, June 16, 2001, 4:58 PM

Robert says:

> The main argument in favor of this feature at all (at least the market
> argument as opposed to "this would be nice to have") is to mimic C++ and
> Java existing code. So to me the only strong argument for nested package
> abstracts would be an argument that they are useful from this point of view.
> I do not see that this is the case, but am not really familiar enough to say.

I'm not sure what you mean.  Are we just trying to interface to C++ and
Java, or mimic them?

I mean, it seems as if in 1980, the designers of Ada-83 might say, "The
main argument in favor of recursive procedures is to mimic Pascal or
Lisp existing code."  It's true, but I would rather say, "The 'recursive
procedure' feature is an important thing in and of itself."  And oh by
the way, notice how the designers of Pascal and Lisp and etc agree with
that.

Likewise, here, I hope you mean, "Mutually recursive type decls are
important in and of themselves.  And oh by the way, you can see how the
designers of Java and C++ must have agreed with that point (because they
support it, too)."

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

From: Robert Dewar
Sent: Saturday, June 16, 2001, 5:17 PM

<<I'm not sure what you mean.  Are we just trying to interface to C++ and
Java, or mimic them?
>>

Sorry, I meant providing the necessary facilities for interfacing to
C++ and Java.

<<Likewise, here, I hope you mean, "Mutually recursive type decls are
important in and of themselves.  And oh by the way, you can see how the
designers of Java and C++ must have agreed with that point (because they
support it, too)."
>>

Nope, that's not what I said. I see no significant market demand for
adding facilities of this type to Ada itself (i.e. I cannot see that it
would significantly affect the use of Ada to add this feature). The only
market requirement I see here is to provide effective interface to C++
and Ada.

I must say that new features emanating from a small group of language
design enthusiasts make me a bit suspicious, I would be more condfident
if it was clear that features suggestions like this were coming from
applications programmers working on real programs.

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

From: Randy Brukardt
Sent: Tuesday, July 3, 2001, 4:57 PM

THIS programmer (with his feet in both camps) finds this a very important
feature to add to Ada 95. The Claw GUI Builder has to use name references
rather than actual access pointers from one kind of object to another. That's
because trying to put all of the types in one package would make a
prohibitively large package (over 30,000 lines). These name references have
been a continual source of bugs; if real access types had been used instead,
most of these bugs could not have happened. (Most of the trouble happens when
a name is changed; that requires searching out any possible place that the name
was stored and changing it. Additionally, it prevents reusing names even when
it otherwise would be safe.)

If the ARG would come to an agreement on this problem, I would like to
re-engineer the builder to eliminate the name references, even if I have to
implement the entire complex feature in Janus/Ada.

---

What worries me with this is that every proposal that has been made has been
riddled with problems. I think we've looked at this long enough to be debased
of the notion that there is any simple solution to this problem.

The only question is whether ANY usable solution can be found. (I haven't seen
one yet.) This issue will make-or-break the amendment process, and right now,
we're WAY on the "break" side.

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

From: Robert Dewar
Sent: Tuesday, July 3, 2001, 7:20 PM

... and of course if at least one Ada implementor implements the feature, it
is always important to realize that the fact that the ARG designs an extension
does not mean that anyone will implement it.

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

From: Randy Brukardt
Sent: Tuesday, July 3, 2001, 7:38 PM

True. In my case, of course, since I have my own personal Ada compiler :-),
I can insure that it gets implemented so I can use it. But I realize that
doesn't mean anyone else will.

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

From: Robert Dewar
Sent: Tuesday, July 3, 2001, 7:55 PM

Of course you are free to add any gizmos you like to your own personal
Ada compiler :-)

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

From: Dan Eilers
Sent: Wednesday, June 20, 2001, 5:04 PM

I agree with Tucker et al that the "with type" proposal (AI 217) is
misguided, because it involves implicit type declarations and their
enclosing "ghost" library units.  These implicit declarations
provide no place to attach pragmas, representation clauses or
access type declarations.  They also don't work well in library-based
implementations, because such implementations can't easily go looking
for the source to an uncompiled unit to see what types are declared
inside it.  Instead, library-based implementations would probably need
some new compilation mode that enters the ghost library packages into
the library.

While the "package abstracts" proposal (AI 271) solves these problems,
I agree with Ed Schonberg et al that this solution is too heavy.  It
increases the number of package parts to 4 (abstract, visible spec,
private, body); it adds a new kind of library unit, causing impact to
tools that manipulate libraries, e.g. compilation-order tools; and
it adds a new kind of "with" clause, e.g. abstract with p, or
with p abstract, or with p'abstract.

I agree with Bob Duff et al that the alternative of bolting the package
abstract to the front of the package spec isn't attractive.  It doesn't
reduce the 4 kinds of package parts; it doesn't eliminate the need
for some new compilation mode that enters just the package abstract into
the library (although such a new compilation mode might be easier to
define and implement); and it doesn't eliminate the need for the
new kind of "with" clause.

The essence of the "package abstracts" proposal is that it provides
a place where an incomplete type can be declared whose full declaration
is expected to be found elsewhere.  (The name of the package abstract
provides the indication as to where the full type declaration is expected
to be found.)  But there seems to be no need for a new type of library
unit to do this job.  Instead, a pragma (or spiffy new syntax for
incomplete type declarations if you prefer) would suffice.

Instead of:

   package abstract Employees is
        type Employee;
        type Emp_Ptr is access all Employee;
    end Employees;

You would have:

   package unrelated is
       type Employee;
       pragma completed_elsewhere(Employee, where => Employees);
       type Emp_Ptr is access all Employee;
   end unrelated;

This involves no new package parts, no new library units, and no
new kinds of "with" clauses.  If packages Employees and Departments
were both children of the same parent, then the incomplete
declarations could simply go in the parent.

        -       -       -       -       -       -       -

As a further refinement, I would note that the "where" parameter
to pragma completed_elsewhere is anomalous, since it names a
package that isn't visible, and normally isn't even available in
the library.  I don't think the "where" parameter is actually
necessary.  Instead of the incomplete type declaration indicating
where the full type will be (so that the compiler can ultimately
connect the two), the full declaration could indicate what
incomplete type it is completing.  This could be done with a
pragma (or spiffy new syntax if you prefer).

So instead of:

    package abstract Employees is
        type Employee;
        type Emp_Ptr is access all Employee;
    end Employees;

    package abstract Departments is
        type Department;
        type Dept_Ptr is access all Department;
    end Departments;

    with Departments'abstract;
    package Employees is
        type Employee is private;
        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;
    end Employees;

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

we would have:

   package unrelated is
       type Employee;  pragma completed_elsewhere(Employee);
       type Emp_Ptr is access all Employee;

       type Department;  pragma completed_elsewhere(Employee);
       type Dept_Ptr is access all Department;
   end unrelated;


    with Unrelated;
    package Employees is
        type Employee is private;
           pragma completion(Employee, from => Unrelated);
        type Emp_Ptr is access all Employee;
        procedure Assign_Employee(E : access Employee;
          D : access Unrelated.Department);
        ...
        function Current_Department(D : access constant Employee) return
          Unrelated.Dept_Ptr;
    end Employees;

    with Unrelated;
    package Departments is
        type Department is private;
           pragma completion(Department, from => Unrelated);
        type Dept_Ptr is access all Department;
        procedure Choose_Manager(D : access Department;
          Manager : access Unrelated.Employee);
        ...
    end Departments;


This opens up the possibility of more than one completion of the same
incomplete type, which may be unnecessary, but doesn't seem harmful.

To summarize:
   no new kinds of library units
   no new kinds of package parts
   no new kinds of with clauses
   no impact to compilation-order tools
   no new compilation modes
   no forward references to uncompiled library units
   no problems with library-based implementations
   no new syntax unless we want it (we probably do).

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

From: Randy Brukardt
Sent: Tuesday, July 3, 2001, 6:14 PM

Re: Dan Eiler's proposal.

I'm quite surprised that no one seriously has looked at this proposal. It seems
to me that it eliminates some of the problems of the abstract package and
simplifies it a lot without necessarily losing the important features.

The basic proposal is to introduce a new kind of incomplete type that NEVER
needs to be completed. It would have syntax like (Dan used pragmas, but they're
really ugly for something that will be commonly used):

    type <Defining_Identifier> is separate;
    type <Defining_Identifier> is tagged separate;

The rules for packages would be unchanged. The tagged kind of separate
incomplete type would allow 'Class and use as the type name in parameter
declarations (but not function returns).

My immediate reaction was that this would be hard to implement. But, on further
reflection, I realized that as long as the incomplete rules were enforced
everywhere that the incomplete type is visible, the compiler NEVER needs to
know the real type involved. Moreover, this type could not be used in an
allocator or a generic, so we would get the restrictions we need for free.
(And it is rather like an incomplete type deferred to a body, for which the
compiler can't assume anything anyway.)

The completion would include a new clause in the type declaration:

    type <Defining_Identifier> [completion_clause] ...

    completion_clause ::= for <type_name>

[I'd rather use "completes" here, but that would introduce a new reserved word.
I also looked at "declare" for this use, but it reads funny. I'm sure someone
will have nifty syntax for this.]

There would be a post-compilation check that all "is separate" types are
completed exactly once. (I don't think that there is any benefit to allowing
multiple completions, because the types have to be considered the same in some
scopes, and that could lead to a situation where A = P and B = P, but A /= B.)

To recast Dan's example (fixing a couple of errors):

    package Partial_Definitions is
       type Employee is separate;
       type Emp_Ptr is access all Employee;

       type Department is separate;
       type Dept_Ptr is access all Department;
   end Partial_Definitions;

   with Partial_Definitions;
   package Employees is
       type Employee for Partial_Definitions.Employee is private;
       type Emp_Ptr is access all Employee;
       procedure Assign_Employee(E : access Employee;
          D : access Partial_Definitions.Department);
        ...
       function Current_Department(D : access constant
          Employee) return Partial_Definitions.Dept_Ptr;
   end Employees;

   with Partial_Definitions;
   package Departments is
       type Department for Partial_Definitions.Department is private;
       type Dept_Ptr is access all Department;
       procedure Choose_Manager(D : access Department;
          Manager : access Partial_Definitions.Employee);
        ...
   end Departments;


Now to look at the possible problems:

Determining the representation of Partial_Definitions.Emp_Ptr would be a
problem for some implementations that select the representation of an access
type based on the designated type. However, this would be no different than
doing that for a type whose completion is deferred to the body. So such
implementations must already be able to handle this.

Similarly, Partial_Definitions.Emp_Ptr would never be frozen, but again we
already have to handle that.

Partial_Definitions.Emp_Ptr would never have allocators defined for it.
Probably it would be best that it has no pool at all. That means that any
allocations would have to be converted to the type. Similarly,
Unchecked_Deallocation couldn't be instantiated for it. OTOH, other uses would
not need conversions. So that doesn't seem too bad. If it is a real problem,
AI-230 may help solve that. Or we could simply define such types to be
equivalent (that couldn't be a compatibility problem, as Ada 95 has no such
types!). But then we'd have to deal with defining the pool for the incomplete
access type.

The interesting question is exactly what does the "completion_clause" mean.
The idea appears to be that inside of package Employees,
Partial_Definitions.Employee and Employees.Employee are considered to be the
same type for all purposes including resolution. (That's how we get conversions
between Partial_Definitions.Emp_Ptr and Employees.Emp_Ptr.) Whether that should
hold outside of Employees at places that have visibility on both Employees and
on Partial_Definitions is an open question. Off-hand, I don't see how this
could cause a problem, but perhaps someone else can.


So, at first look, this seems like a useful idea to pursue. It doesn't have any
elaboration or "new unit kind" problems as package abstract does, and it
probably doesn't have the visibility issues either (depending on the exact
definition of when the types are considered the same). It's similar to the
current "with type" in that the access type equivalence isn't perfect, but it
looks much easier to fix than rather than with "with type".

So, what is wrong with this idea? Should it be written up completely?

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

From: Robert Dewar
Sent: Tuesday, July 3, 2001, 7:55 PM

I certainly prefer the separate idea to the package abstract proposal from
a language design point of view, but it is not clear to me that it satisfies
all the Java interface requirements. I definitely think it is worth writing
up the idea completely.

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

From: Randy Brukardt
Sent: Tuesday, July 3, 2001, 8:19 PM

Humm. The only Java interface requirement that I know of that we're trying to
address with this proposal is the mutually dependent types problem. And Java
types are essentially tagged types in Ada, so with the tagged incomplete type,
we get the needed functionality. I think. Is there something else that I've
missed?

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

From: Ted Baker
Sent: Thursday, July 5, 2001, 7:05 AM

I've been lurking out here watching the discussion on the package abstract
idea, dismayed by the conceptual complexity and ugliness.  The Dan/Randy
separate proposal seems much cleaner.  I hope you can work it out to everyone's
satisfaction.

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

From: Tucker Taft
Sent: Monday, July 9, 2001, 3:51 PM

Ted Baker wrote:
>
> I've been lurking out here watching the discussion on the package abstract
> idea, dismayed by the conceptual complexity and ugliness.  The Dan/Randy
> separate proposal seems much cleaner.  I hope you can work it out to everyone's
> satisfaction.  --Ted

I agree that Dan's proposal, with some of Randy's suggestions,
is a direction worth pursuing.  This is not too far from
a direction suggested in the past using pragma Import
and pragma Export applied to types, analogous to the
trick that can be used to implement a subprogram declared
in one package with a subprogram declared in another package.

I would like to know at least the name of the
package where the full type definition is provided,
and I wouldn't mind requiring it to be a child
of the package where the incomplete type is declared,
ensuring that the incomplete/separate type's declaration
is directly visible at the point of the full type definition.
I would require that the name of the full type in the child
match the name of the incomplete type in its parent.

Hence, something like:

   package P is
       type T is separate(C);  -- is completed
                               -- in child "C" of P.
       ...
   end P;

   package P.C is
       type T is ...
   end P.C;

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

From: Randy Brukardt
Sent: Friday, July 13, 2001, 6:15 PM

> I agree that Dan's proposal, with some of Randy's suggestions,
> is a direction worth pursuing.  This is not too far from
> a direction suggested in the past using pragma Import
> and pragma Export applied to types, analogous to the
> trick that can be used to implement a subprogram declared
> in one package with a subprogram declared in another package.

Except this is a model that makes sense, while using pragma Import and Export
says KLUDGE in large, flashing letters.

> I would like to know at least the name of the
> package where the full type definition is provided,
> and I wouldn't mind requiring it to be a child
> of the package where the incomplete type is declared,
> ensuring that the incomplete/separate type's declaration
> is directly visible at the point of the full type definition.
> I would require that the name of the full type in the child
> match the name of the incomplete type in its parent.
>
> Hence, something like:
>
>    package P is
>        type T is separate(C);  -- is completed
>                                -- in child "C" of P.
>        ...
>    end P;
>
>    package P.C is
>        type T is ...
>    end P.C;

Well, this is almost OK, but I think it has some problems.

First, describing the semantics of the name in the separate is going to be
interesting. It has to reference something not declared yet, and about all we
can say about it is that it is a library unit -- which doesn't even need to
exist yet. Indeed, there must be no requirement that it exist.

Second, restricting the completion to a child is a non-starter. I certainly
don't think people would be happy to have to rename all of their packages in
order to use this feature. It is particularly bad, because the parent
(presumably with the original name) would contain just the partial definitions,
while the child would contain the "meat" -- the stuff that exists now. So every
reference the package would have to be changed.

Moreover, I don't think such a restriction would work well with Robert Dewar's
"only reason for this feature" -- interfacing to Java. If you have two Java
classes Class_A and Class_B that are mutually recursive, this idea would force
you to introduce not only another package, but one whose name will show up
everywhere:

    package Java_Stuff is
       type Class_A is tagged separate (A);
       type Class_A_Access is access all Class_A;
       type Class_B is tagged separate (B);
       type Class_B_Access is access all Class_B;
    end Java_Stuff;

    package Java_Stuff.A is
       type Class_A is tagged ...
       ...
    end Java_Stuff.A;

    package Java_Stuff.B is
       type Class_B is tagged ...
       ...
    end Java_Stuff.B;

I think it would be much cleaner to say:

    package Abstract_A_and_B is
       type Class_A is tagged separate (A);
       type Class_A_Access is access all Class_A;
       type Class_B is tagged separate (B);
       type Class_B_Access is access all Class_B;
    end Abstract_A_and_B;

    with Abstract_A_and_B;
    package A is
       type Class_A is tagged ...
       ...
    end A;

    with Abstract_A_and_B;
    package B is
       type Class_B is tagged ...
       ...
    end B;

...because then the "abstract" package never appears in uses of Class_A or
Class_B. Only the declarations of Class_A and Class_B need worry about it.

It is especially annoying that there is no technical reason (at least that I
know) for this restriction: it would just be a language designer's feel for
"the best way to do it". There doesn't seem to be any technical need for direct
(or any!) visibility on the incomplete type in order to complete it - the type
has no operations (primitive or otherwise) anyway. And the visibility of uses
doesn't seem to matter either: you just get whatever view you have.

So I would make the name a library unit name, and drop any child requirement.

A third point (just a quibble) is that it really is necessary for readability
to mention that this type is a completion (even if the name of the type is
required to be the same). So, I'd expect to always put in a comment:
    -- Completes Abstract_A_and_B.Class_B.
It seems like it would be helpful to include this in the syntax somehow, just
as is done with subprogram names on "end". (Aside: I sure wish "end record;"
was "end <type_name>;", 'cause that's what I write every time anyway. :-)

Anyway, it seems that I've volunteered again :-) to write this up. I'm not sure
whether to replace AI-271 with this proposal, or to open a third AI for this
third approach. Suggestions?

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


Questions? Ask the ACAA Technical Agent