Version 1.5 of ais/ai-60217.txt

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

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

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

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

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

I was tasked with writing up the "type C.T;" proposal.  Here it
is. [Editor's note: This 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