Version 1.3 of ais/ai-60217.txt

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

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

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

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

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

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

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

Questions? Ask the ACAA Technical Agent