!standard 07.01 (02) 01-09-03 AI95-00271/02 !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. The goal of the proposed features 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. !proposal Three new constructs are proposed: a package "abstract," a "with abstract" 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]; package_abstract_item ::= {basic_declarative_item | package_abstract} A package abstract is a new kind of library unit: library_unit_declaration ::= subprogram_declaration | package_abstract | package_declaration | generic_declaration | generic_instantiation An incomplete type declared in a package_abstract need not have a completion in the abstract. Because a package_abstract has only one list of declarations (no private part), things like private types, private extensions, and deferred constants are illegal in an abstract. The specification of a package must "conform" to its abstract, if any, in the following sense: - Each incomplete type declaration in the abstract of a package must be completed in the visible part of the specification of the package, unless it was completed in the abstract. This completion may be a private_type_declaration or a private_extension_declaration. - Each nested package abstract must be completed in the visible part of the specification by a nested package. This nested package must conform to its abstract. The completion of a (library-level) private package_abstract must be a private package. The completion of a public package_abstract must be a public package. All the entities declared in package_abstract become frozen at the end of the abstract, except for incomplete types. Each library-level package which has no explicit abstract is assumed to have an empty package_abstract with nested package_abstracts appropriate for each package nested in the visible part of the package. WITH ABSTRACT CLAUSE: with_abstract_clause ::= WITH library_unit_package_name ABSTRACT {, library_unit_package_name ABSTRACT}; A with_abstract_clause on a compilation unit makes the abstract of each specified package visible within the compilation unit. A (normal) with clause on a compilation unit makes the specification and the abstract visible. Consider the case where package P has an abstract which declares an incomplete type T. At a place where only the abstract of P is visible, the name P.T designates an incomplete type, and the restrictions of RM95 3.10.1 apply. At a place where the specification of P is visible, the name P.T designates a complete type, and the restrictions of RM95 3.10.1 are not applicable. A with_abstract_clause specifying a package which has no (explicit) abstract has no effect. Within the abstract of a child package the abstract of its parent is directly visible. Within the specification of a child package, the abstract of its parent is directly visible. If a package specified by a with_abstract_clause on some compilation unit is a child package, then the abstracts of its parent and each further ancestor are made visible within the compilation unit. If a package_abstract is made visible by a with_abstract_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. [TBD: We should probably have incomplete-types-completed-by-private-types even in a vanilla package visible part.] [TBD: Should "package abstract P renames Q;" be supported? Seems OK.] 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. 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. 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. More precisely, when compiling a compilation_unit that says "with P;" the compiler needs to look at both the visible and private parts of P. This proposal adds 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 visibility rules work as usual, considering an abstract to come before the visible part/private part/body. !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; 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; !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; -- < ... > 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 package P is I suggest: package P is abstract 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 > > 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: Randy Brukardt 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 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 <> Sorry, I meant providing the necessary facilities for interfacing to C++ and Java. <> 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). [Editor's note: Further discussion of this option appears in AI-00277.] ************************************************************* From: Pascal Leroy Sent: Tuesday, July 17, 2001, 7:11 AM 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 have an entirely different perspective here. I don't think Rational will implement interfacing with C++ or Java in the foreseeable future (I don't think we have seen much demand). On the other hand, mutually dependent types show up all the time in OO designs (in particular when mapping a UML model to Ada). So I believe we need a general solution to this issue, regardless of interfacing consideration (general doesn't mean overengineered, of course). I guess different vendors see different user communities... ************************************************************* From: Erhard Ploederedere Sent: Wednesday, July 18, 2001, 6:56 AM What I would like to see at some point is one write-up that summarizes the pros and cons of the three or more approaches in a single document. With three or more separate write-ups, these comparisons are harder to make. I guess I am asking for a summarizing Study Note rather than multiple AIs, which describe a single approach each and collect random comments in the appendix. This study note may well be the eventual "master AI-171". ************************************************************* From: Randy Brukardt Sent: Wednesday, July 18, 2001, 2:26 PM Sounds wonderful. Are you volunteering to write it? (So far, with this proposal, the person who has floated an idea has been tagged to write it up.) I know that the limited time I have available will prevent me from doing anything like that until at least November. In the mean time, we're going to have a meeting, and I need to rewrite several AIs (including the "type separate" proposal). We're going to need something to discuss in the mean time. If you want the simple version, here it is: "with type" Con: Can't handle access types (due to representation issues). Implementation needs "psuedo packages" internally. What happens when multiple paths to the same item "meet"? Pro: Easy to describe to users. "package abstract" Con: Heavy, complex mechanism. Complex visibility issues to work out. New kind of compilation unit affects all tools (alternatives avoiding compilation unit has similar effect on tools because the source must be compiled in two different ways). Pro: Handles access types cleanly. "type separate" Con: Must introduce extra packages (either "unnecessary" parents with Tucker's rules or "unrelated" ones with Dan's/Randy's rules). No allocators/deallocators on access types with designated types of this kind. (Probably others, once a proposal is made and picked apart.) Pro: No new kind of units. No new visibility issues. Access types can be handled (representation clauses can be given if needed). Model is easy to understand (it is an extension of existing rules). No run-time implementation problems; the implementation is identical to incomplete-completed-in-body. ************************************************************* From: Pascal Leroy Sent: Thursday, September 13, 2001, 3:24 AM I have updated AIs 195, 229, 255 and 271 which were on my action items list. The updated AIs are on the web site. 271 was updated to reflect Bob's detailed analysis of the last cut of this AI. So now the abstract is a separate compilation unit again (I actually started from Tuck's original write-up) and I have removed most of the restrictions (Bob's message was full of questions of the kind "why do we want to disallow this?" and I must say that I was unable to answer most of these questions). I am sure this proposal is going to look a bit extreme to some people, but at least it's a basis for discussing the topic at the next meeting. Please, no quibbling on the syntax: it's much more important to look at the semantics at this point. ************************************************************* From: Tucker Taft Sent: Thursday, September 13, 2001, 3:55 PM It actually looks pretty good, now that all the restrictions are removed. Your example needs a minor tweak -- you are repeating the declaration of the access types. I don't believe repeating the declaration is required (nor is it allowed) with the new proposal. I'm not sure whether you answered the question of about the legality of a "with P abstract" when a "with P" was already in scope. I think I had suggested that a "with P abstract" should be illegal if a "with P" was already applicable. I suppose since you didn't disallow it, it is legal, but it might be worth a note. One reason to disallow it is that when you see a "with P abstract" on a package Q one tends to presume that "P" may safely "with Q". However, if the abstract of Q had a "with P" then it would not be legal for P to "with Q." Hence, allowing the (redundant) "with P abstract" on Q could turn out to be misleading, which is something we generally try to minimize. (I'll admit that this is not totally compelling. ;-) ************************************************************* From: Pascal Leroy Sent: Friday, September 14, 2001, 3:23 AM > Your example needs a minor tweak -- you are repeating > the declaration of the access types. I don't believe repeating > the declaration is required (nor is it allowed) with the new proposal. True. The example needs fixing. > I'm not sure whether you answered the question of about the legality of > a "with P abstract" when a "with P" was already in scope. > I think I had suggested that a "with P abstract" should be > illegal if a "with P" was already applicable. I suppose > since you didn't disallow it, it is legal, but it might > be worth a note. Yes, my intent was that the "with P abstract" should have no effect. I could live with making it illegal, but... > One reason to disallow it is that > when you see a "with P abstract" on a package Q one tends > to presume that "P" may safely "with Q". However, if > the abstract of Q had a "with P" then it would not be legal > for P to "with Q." Hence, allowing the (redundant) "with P abstract" > on Q could turn out to be misleading, which is something > we generally try to minimize. (I'll admit that this is > not totally compelling. ;-) ... it is currently legal for the spec and body of Q to have a "with P", and I guess you could apply the same reasoning: when you see "with P" on the body of Q, you could assuming that P may safely "with Q", but it's not the case. So your argument is indeed "not totally compelling". *************************************************************