!standard 10.1.2 (01) 99-03-23 AI95-00217/01 !class amendment 99-03-23 !status work item 99-03-23 !status received 99-03-23 !priority Medium !difficulty Hard !subject Handling Mutually Dependent Type Definitions that Cross Packages !summary A new kind of "with" clause, the "with type" clause, is proposed to support mutually dependent type definitions that cross packages. !problem Ada 95 does not allow two packages 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 definitions. When expressed in Ada, this means that a package may define a type which is used in a second package, while also making use of a type defined in that package. 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 with_type_clause ::= WITH TYPE [ancestor_library_unit_name.] package_identifer . type_identifier ; | WITH TYPE [ancestor_library_unit_name.] package_identifer . type_identifier ; IS ACCESS [ ALL | CONSTANT ] subtype_mark ; In the first form of the with_type_clause, the name [ancestor_library_unit_name].package_identifier.type_identifier is introduced as the name of an incomplete type that is presumed to be declared in the package [ancestor_library_unit_name].package_identifier. However, no semantic dependence is created on this package, and in fact the package need not be present in the environment at the time the with_type_clause is processed. If present, the ancestor_library_unit_name is treated as though it appeared in a "normal" with clause, meaning that the corresponding library unit must exist, and a semantic dependence is created upon it. In the second form, the name for an access type is introduced with the specified designated subtype. The subtype_mark may include 'Class, and may refer to an incomplete type introduced by an earlier with_type_clause. Note that no constraint is permitted on the designated subtype specification, due to the use of subtype_mark rather than subtype_indication. Any compilation unit that has a semantic dependence on another compilation unit which has a "with_type_clause", as well as a dependence on the package identified by the with_type_clause, must verify that the named type does in fact appear within the visible part of the package. If the second form of with_type_clause was used, it must also verify that the type is an access type with a statically matching designated subtype. !wording !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 type Departments.Department; package Employees is type Employee is private; procedure Assign_Employee(E : access Employee; D : access Departments.Department); ... end Employees; with type Employees.Employee; package Departments is type Department is private; procedure Choose_Manager(D : access Department; Manager : access Employees.Employee); ... end Departments; Note that if the Employee type needs to have a field in which it can store an access value designating its department, then we will need to add a "with type" for a named access-to-department type, or define our own, perhaps in the private part of the package. !discussion This proposal attempts to resolve problems encountered 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 definitions. When expressed in Ada, this means that a package may define a type which is used in a second package, while also making use of a type defined in that package. 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. The "with_type" clause eliminates the cyclic dependence problem by allowing a name for an incomplete or access type to be introduced without needing those type definitions to have been elaborated. The existing limitations on incomplete types, and access-to-incomplete types, are sufficient to protect against inappropriate usage of such types prior to their full elaboration. One alternative approach would be to use pragma Import to import the full definition of a type from some other package, without creating a semantic dependence on that package. For example: type T; pragma Import(Ada, T, External_Name => "Some_Package.T"); would introduce an incomplete type named T and then specify that its full definition appears in Some_Package. Similarly, an access type could be declared, and then imported from some other package. Again, any compilation unit that depends both on the package where such a pragma Import appears, as well as on the package identified in the External_Name, would have to make sure that the type definitions were compatible. Furthermore, it would have to treat the types as aliases for one another, allowing free interconversion between the two. An advantage of this pragma Import approach is that it requires no new syntax, nor any new mechanisms for introducing names. However, having the Import have the effect of creating a type equivalence is certainly stretching the original intent for Import, and would clearly require extra work to support in the compiler (though probably less than that required for the "with type" approach). A disadvantage of this pragma Import approach is that it creates implicit connections between compilation units which are not visible in the context clause. Also, if the imported type is given the same identifier as it has in the package where its full definition appears, then compilation units that "use" both packages will find the type names hiding one another. !appendix Randy Brukardt 99-03-29 I've reformatted Tucker's submission into the format discussed at the recent ARG meeting. Note that I haven't made any of the changes considered at that meeting. *************************************************************