!standard 10.01.02 (03) 03-02-04 AI95-00217-06/01 !standard 10.01.02 (04) !standard 10.01.02 (08) !standard J.10 (00) !class amendment 03-02-04 !status work item 03-02-04 !status received 03-02-04 !priority Medium !difficulty Hard !subject Limited With Clauses !summary A new kind of with_clause is proposed. !problem Ada allows mutually recursive types to be declared only 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 feature is to allow mutual recursion among type declared in separate packages (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 undue restrictions on the programmer. For mutually recursive types, it is valuable if subprogram parameters may be of the type itself, rather than only an access to the type. However, for most types, it may be necessary to see the full definition to know how parameters of the type are passed. However, because tagged types are always passed by reference, there is no implementation difficulty in allowing them to be used as parameters even when the full definition of the type is not available. Hence, it makes sense to relax the rule for using incomplete types that are known to be tagged, to allow them as formal and actual parameters, since from a code generation point of view they are essentially equivalent to access parameters. !proposal A new kind of with_clause is added, of the form "limited with ;". This is called a "limited_with_clause". The old fashioned kind is called an "unlimited_with_clause". There are two key features of a limited_with_clause such as "limited with X;": - It introduces no semantic dependence on X (and hence no elaboration dependence on X). The implementation may choose to elaborate X either before or after the current unit. [In the wording below, I ended up saying it introduces a semantic dependence on the limited view of X.] - Instead of providing visibility to the named packages and their content, it provides visiblility only to the package names, any packages nested within them, and a view of each type therein. The type view is incomplete, implying all the usual restrictions on incomplete types. If the type is tagged, the view is tagged incomplete. Thus, cyclic chains of with_clauses are allowed, so long as the chain is broken by at least one limited_with_clause. !wording 10.1.2(4) says: with_clause ::= with library_unit_name {, library_unit_name}; Change to: with_clause ::= limited_with_clause | unlimited_with_clause limited_with_clause ::= limited with library_unit_name {, library_unit_name}; unlimited_with_clause ::= with library_unit_name {, library_unit_name}; Add a legality rule: A library_item mentioned in a limited_with_clause shall be a package_declaration[, not a subprogram_declaration, generic_declaration, or generic_instantiation]. Discussion of existing wording: It is natural to think of a with_clause as causing a library unit to become visible. However, that's not what the RM says. Instead, the RM says that *lack* of a with_clause causes a library unit *not* to be visible. That's kind of odd, but to understand the wording below, you have to understand the existing model in this language-lawyerly fashion. The current RM defines the notion of an "environment declarative_part". During compilation, this fanciful declarative_part is imagined to contain all the library_items of interest. The order of these library_items is such that there are no forward semantic dependences. With_clauses introduce semantic dependences, so they control the order. Semantic dependences are also caused by child-parent and body-spec relationships. Subunits are imagined to be "inlined" at their stub point, so subunits can be ignored for visibility purposes. The visibility rules for library units come from treating the environment declarative_part in the usual way, with one difference: In a normal declarative_part, you can see everything that comes before, and nothing that comes after (with a bunch of hiding rules thrown in as well). In the environment declarative_part, however, you can see things that come before, but only if mentioned in a with_clause. Similar oddities occur in the definition of "immediate scope" and "scope". I'm not sure what's the best way to introduce limited_with_clauses into this model. One idea is to say that "limited with X" means you can see X even if X occurs later in the environment declarative_part. This would introduce (conceptual) forward references, something never seen in Ada before. Another idea is to say that there is an implicit declaration of a "limited view of package X" (occurring before any "limited with X"'s). The normal declaration of X then appears later in its usual place. Either way, "limited with X" introduces a very limited view of X that only includes nested packages and incomplete types. I think I'll try the latter idea, even though I hate to introduce yet more magical implicit junk. Here goes: 10.1.4(1-3) says: 1 Each compilation unit submitted to the compiler is compiled in the context of an environment declarative_part (or simply, an environment), which is a conceptual declarative_part that forms the outermost declarative region of the context of any compilation. At run time, an environment forms the declarative_part of the body of the environment task of a partition (see 10.2, ``Program Execution''). 2 The declarative_items of the environment are library_items appearing in an order such that there are no forward semantic dependences. Each included subunit occurs in place of the corresponding stub. The visibility rules apply as if the environment were the outermost declarative region, except that with_- clauses are needed to make declarations of library units visible (see 10.1.2). 3 The mechanisms for creating an environment and for adding and replacing compilation units within an environment are implementation defined. Add some text somewhere in there: In addition, for each library_package_declaration in the environment, there is a conceptual declaration of a "limited view" of that library package. [Note: This animal is sort of what folks have been calling a "package abstract", except that it is automatically generated (or imagined) by the compiler.] The limited view of a package contains: - A declaration of the limited view of each nested package. - For each type_declaration in the visible part, an incomplete_type_declaration with the same name, whose completion is the type_declaration. If the type_declaration is tagged, then the incomplete_type_declaration is tagged incomplete. The limited view of a package has the same identifier as the package_declaration. There is no syntax for declaring limited package views, because they are always implicit. Nonetheless, the declaration of a limited package view is considered to be a library_item. A name mentioned in a limited_with_clause denotes the limited view declaration. I guess we need to make sure the definition of "mentioned" is appropriate: for a limited_with_clause, we are "mentioning" the limited view. A library_package_declaration is considered to be the completion of its limited view declaration. 10.1(26) says: 26 A library_item depends semantically upon its parent declaration. A subunit depends semantically upon its parent body. A library_unit_body depends semantically upon the corresponding library_unit_declaration, if any. A compilation unit depends semantically upon each library_item mentioned in a with_clause of the compilation unit. In addition, if a given compilation unit contains an attribute_reference of a type defined in another compilation unit, then the given compilation unit depends semantically upon the other compilation unit. The semantic dependence relationship is transitive. Add some new cases: - A limited library package view declaration depends semantically upon the parent limited package view declaration. - A library package declaration depends semantically upon its limited view. - The part about with_clauses should work unchanged: for a limited_with_clause, the dependence is upon the limited view; for an unlimited_with_clause, the dependence is upon the package_decl. There are various places in Chapter 8 that refer to "the declaration of a library unit", presuming that there's only one. We need to change the wording, since library packages now have two declarations: the limited view, plus the normal package_decl. With minor wording changes, most of it should work OK, because: A library_package_decl is the completion of its limited view, and therefore hides it. The incomplete types in the limited view are completed by the regular types. This introduces the possibility of completing an incomplete type with a private type, but I think all the proposals are going to have a similar problem (if it's a problem). !discussion To review this proposal, one should search the RM for the following terms, to see if they still make sense: - with_clause - semantic dependence, depend[s] semantically Also study chaps 8 and 10. ---- Here's one possible implementation method: Whenever the compiler sees "limited with X", it reads the source file containing X, parses it, and collects a list of nested package and type names, and whether they're tagged. It is important that this process can be done without doing any "serious" semantic analysis, and without looking at anything imported by X. The compiler then constructs the limited view of X. Luckily, taggedness of types can be detected syntactically. At any place where a package_decl is visible, the compiler needs to be sure the limited view of it is hidden. The compiler can avoid reconstructing the limited view of X, if it likes, if it knows the source has not changed. For compilers that save program library information on disk, I presume the limited view would have to be saved on disk in a similar manner. In Rational terms, this means each source file has (up to) *two* compilation artifacts. I presume all compilers have some subroutine to check whether two types are the same type (in case of incomplete types and the like). And/or a subroutine to determine which view of a type should be used. These subroutines need to be changed to deal with this new flavor of incomplete type. But I would expect that to be fairly localized; the calls to those subroutines should still work. ---- We do not allow a limited_with_clause to mention a procedure, because that would allow calling a procedure before its spec is elaborated. The language has no mechanism to deal with that. Similarly, we do not allow a limited_with_clause to mention a generic, because that would allow instantiating a generic before its spec is elaborated. We do not allow a limited_with_clause to mention a generic instantiation, because that would require all kinds of semantic analysis stuff, such as visibility. ---- A package could say "limited with A.B.C;" and then its child could say "with A.B.C;". Now we're within the scope of the real things, so the limited views would be hidden. Similarly, if P says "limited with P.C", we're within the scope of the real P, so the limited view of it is hidden. We have limited access to C. ---- Consider: package A is type T is range 1..10; end A; with A; use A; package B is X, Y: T; end B; limited with A; with B; use B; package C is X := Y; end C; OK, assignment statements are syntactically illegal in a package spec. But think of some other thing that depends on the type. Perhaps, "Flag: Boolean := X in 1..10;". Is this legal? It would be odd if adding "limited with A;" reduced ones capabilities. !example !ACATS test !appendix From: Pascal Leroy Sent: Monday, February 3, 2003 5:19 AM A while back, Dan wrote, as a side comment: "Well, the nicest solution for the user is just to allow specs to with each other, as other languages do." This got me thinking, as this is an approach that we have discussed briefly once or twice, but writers of library-based compilers (like me) started to yell and scream that it would be impossible to implement. Well, I am starting to think that all approaches considered so far are nearly impossible to implement, and furthermore are quite hard to use. So I started to investigate a solution where units are allowed to with each others. Here are my half-baked ideas: 1 - A new form of context clause is added: weak [private] with library_unit_name {, library_unit_name} Weak with clauses don't have to form an acyclic graph. 2 - A compilation unit is added to the environment in two phases. During the first phase we say that the unit is "superficially added"; during the second phase that it is "fully added" (better terminology welcome). How this is achieved is not specified by the language. 3 - To superficially add a unit to the environment, there is no requirement that any other unit already exists in the environment. (Not even its parent, heck, not even Standard!) 4 - When a unit is superficially added to the environment, the only names that can be referenced from outside the unit are those of packages (both the unit itself and nested packages) and of types. Moreover, every type is at this point considered to be an incomplete type, and subject to the restrictions mentioned in 3.10.1; the taggedness of the types is visible. (The alert reader may notice some similarity between a unit superficially entered into the environment and a package abstract.) 5 - To fully add a unit to the environment, all of the units upon which it depends semantically must already have been fully entered into the environment. Furthermore, all of the units that it names in a weak with clause must already have been superficially entered into the environment. The only names that can be referenced from such units are those of types and packages, and types are considered incomplete. 6 - To fully add a unit to the environment, this unit must be made of the same lexical elements (except for comments) as when it was superficially added to the environment. 7 - The celebrated textbook example found in the AI is then simply written as follows: weak with Departments; package Employees is type Dept_Ptr is access all Departments.Department'Class; type Employee is tagged private; procedure Assign_Employee(E : in out Employee; D : in out Departments.Department); ... function Current_Department(D : in Employee) return Dept_Ptr; end Employees; weak with Employees; package Departments is type Emp_Ptr is access all Employees.Employee'Class; type Department is tagged private; procedure Choose_Manager(D : in out Department; Manager : in out Employees.Employee); ... end Departments; 8 - Marketing hype a la Tucker: - No three-part types - No new compilation units - No new declarations - No changes to the visibility rules - Little new syntax - Support for circularities involving types in nested packages I am no expert in source-based compilers, but I believe that this model should be quite close to what GNAT currently does for "with type". Superficially adding a unit to the environment would merely mean that the source file exists, and the "weak with" would give the compiler permission to peak at it. For library-based compilers this proposal adds some complexity, but not necessarily more than those proposals which change the visibility rules or otherwise break the invariants upon which the compilers depend. In the case of our implementation a tree in the library can be in two states: parsed (as produced by the parser, no names resolved) or analyzed (with all the decorations produced by the semantic analyzer, names resolved, etc.). These states would correspond directly to a unit superficially/fully entered in the environment. We would need to beef up the parsed representation a bit to add (1) tables listing the types and package names, and (2) a hash code representing the sequence of tokens in the unit (to perform the check of #6 above). Intense hand-waving here, as I have not done anything resembling a serious design. Feel free to explain why this is a dumb idea... ************************************************************** From: Arnaud Charlet Sent: Monday, February 3, 2003 5:35 AM I certainly like Pascal's proposal (weak with) very much. It is the only proposal (apart from the old with type that had its bunch of issues) so far that looks appealing to me. It addresses well the issue of interfacing with other languages and from a user point of view, it is very close to an ideal solution. As Robert said, we don't have any real technical constraint in GNAT to implement any of the proposed solution. This one would probably be close to the current with type implementation. I am not sure the wording about adding a unit in the environment, and requiring compilers to have two (or more for that matter) phases should be stated as such, but I guess this can probably be sorted out without too much difficulty if this proposal gets some more supporters. ************************************************************** From: Robert Dewar Sent: Monday, February 3, 2003 6:39 AM Pascal's proposal for weak with is the first one I have seen since WITH TYPE that I like, and in fact I like it better than WITH TYPE (I am not taling deep semantics here, just a surface impression). It also seems to satisfy the requirement for easy generation of equivalent Ada from e.g. Java stuff. ************************************************************** From: Robert Dewar Sent: Monday, February 3, 2003 6:42 AM By the way I don't like "weak" very much because it has the wrong connotations to be. The issue is that this with is preliminary or limited (so if you are in a mood to reuse keywords. limited with x; is more like it ... A more radical proposal is that all WITH's that meet the criteria are weak -- could that work? ************************************************************** From: Pascal Leroy Sent: Monday, February 3, 2003 6:51 AM I am not wedded to a particular syntax. If we think that the idea is worth pursuing, we can probably come up with an acceptable syntax (we don't lack creativity in the area of syntax). > A more radical proposal is that all WITH's that meet the criteria are > weak -- could that work? From a methodological standpoint, I'd say that introducing a circularity should be the result of a conscious decision, not something that happens by chance. So the user should have to make it explicit that a circularity is what she wants--by typing "weak", "limited" or whatever. ************************************************************** From: Robert Dewar Sent: Monday, February 3, 2003 7:03 AM I am not so sure I agree with this. At one level of abstraction you WITH something because you need something in it, and it is not really your business if it needs something in yourself. ************************************************************** From: Jean-Pierre Rosen Sent: Monday, February 3, 2003 7:02 AM > limited with x; > > is more like it ... > Or even with X abstract; ;-) ************************************************************** From: Robert A. Duff Sent: Monday, February 3, 2003 8:18 AM > A while back, Dan wrote, as a side comment: "Well, the nicest solution for the > user is just to allow specs to with each other, as other languages > do." Dan was exactly correct. That's why I can't get too excited about Tucker's aesthetic gut feelings about the various other proposals -- they are *all* less aesthetically pleasing than the "right" solution. >...This > got me thinking, as this is an approach that we have discussed briefly once or > twice, but writers of library-based compilers (like me) started to yell and > scream that it would be impossible to implement. Well, I am starting to think > that all approaches considered so far are nearly impossible to >implement, ... That seems exaggerated, ... >...and > furthermore are quite hard to use. So I started to investigate a solution where > units are allowed to with each others. Here are my half-baked ideas: > > 1 - A new form of context clause is added: > > weak [private] with library_unit_name {, library_unit_name} > > Weak with clauses don't have to form an acyclic graph. This is the solution I chose for my from-scratch language design I do as a hobby. My syntax is "import X;" to import X and require X to be elaborated earlier than this unit -- essentially the same as Ada's "with X;" And "import forward X;" to import X and elaborate X *after* this unit. This is essentially the same as your "weak with X;". ("import forward" is like a forward reference.) The only thing needed in the RM is the syntax for weak with's, make sure the rule about no cycles applies only to cycles formed by strong with's. All the stuff below is really a description of how the Rational compiler would work (and others with a similar library model). There's no need for any of it in the RM. (Aside: In my "hobbyist" language, I am able to achieve complete *compile-time* checking of elaboration order problems, even in the case of indirect/dispatching calls, with no sacrifice in expressive power. I thought that was pretty cool.) > 2 - A compilation unit is added to the environment in two phases. During the > first phase we say that the unit is "superficially added"; during the second > phase that it is "fully added" (better terminology welcome). How this is > achieved is not specified by the language. > > 3 - To superficially add a unit to the environment, there is no requirement that > any other unit already exists in the environment. (Not even its parent, heck, > not even Standard!) > > 4 - When a unit is superficially added to the environment, the only names that > can be referenced from outside the unit are those of packages (both the unit > itself and nested packages) and of types. Moreover, every type is at this point > considered to be an incomplete type, and subject to the restrictions mentioned > in 3.10.1; the taggedness of the types is visible. (The alert reader may notice > some similarity between a unit superficially entered into the environment and a > package abstract.) > > 5 - To fully add a unit to the environment, all of the units upon which it > depends semantically must already have been fully entered into the environment. > Furthermore, all of the units that it names in a weak with clause must already > have been superficially entered into the environment. The only names that can > be referenced from such units are those of types and packages, and types are > considered incomplete. > > 6 - To fully add a unit to the environment, this unit must be made of the same > lexical elements (except for comments) as when it was superficially added to the > environment. Note in particular that this consistency check is an artifact of your compilation model. That is, your compiler is going to read the same file twice. But I can imagine compilers that will only read the source once, so the "superficial" and "full" distinction is represented purely in memory during compilation. I think any consistency requirements are already covered by the RM's words about not including two different versions of the same unit in a partition. > 7 - The celebrated textbook example found in the AI is then simply written as > follows: > > weak with Departments; > package Employees is > type Dept_Ptr is access all Departments.Department'Class; > type Employee is tagged private; > procedure Assign_Employee(E : in out Employee; > D : in out Departments.Department); > ... > function Current_Department(D : in Employee) return Dept_Ptr; > end Employees; > > weak with Employees; > package Departments is > type Emp_Ptr is access all Employees.Employee'Class; > type Department is tagged private; > procedure Choose_Manager(D : in out Department; > Manager : in out Employees.Employee); > ... > end Departments; Yes, although only one of the with's *needs* to be weak. For symmetry, they probably both should be weak. > 8 - Marketing hype a la Tucker: > > - No three-part types > - No new compilation units > - No new declarations > - No changes to the visibility rules > - Little new syntax > - Support for circularities involving types in nested packages > > I am no expert in source-based compilers, but I believe that this model should > be quite close to what GNAT currently does for "with type". Superficially > adding a unit to the environment would merely mean that the source file exists, > and the "weak with" would give the compiler permission to peak at it. > > For library-based compilers this proposal adds some complexity, but not > necessarily more than those proposals which change the visibility rules or > otherwise break the invariants upon which the compilers depend. In the case of > our implementation a tree in the library can be in two states: parsed (as > produced by the parser, no names resolved) or analyzed (with all the decorations > produced by the semantic analyzer, names resolved, etc.). These states would > correspond directly to a unit superficially/fully entered in the environment. > We would need to beef up the parsed representation a bit to add (1) tables > listing the types and package names, and (2) a hash code representing the > sequence of tokens in the unit (to perform the check of #6 above). Intense > hand-waving here, as I have not done anything resembling a serious design. > > Feel free to explain why this is a dumb idea... Because your evil twin was yelling and screaming about the implementation difficulty of this idea... ;-) ************************************************************** From: Tucker Taft Sent: Monday, February 3, 2003 8:24 AM I like this, although it is more work for us than almost any other proposal. We do all semantics on the fly, and don't have a mode that parses without doing semantic analysis. In general, I believe the "type C.T;" approach solves the basic problem, and will be one of the easiest to implement. As far as syntax, I like the suggestion of using "limited" as a prefix on the "with" clause, and it goes awfully nicely with "private" ;-). [limited] [private] with library_item_name {, library_item_name}; It would be a major headache (dare I say "huge" headache?) for us if the "limited"ness of the "with" were not explicit. ************************************************************** From: Robert A. Duff Sent: Monday, February 3, 2003 8:34 AM Robert wrote: > A more radical proposal is that all WITH's that meet the criteria are > weak -- could that work? No, I don't think so. The problem is that if there's a cycle of with's, the compiler needs to choose *one* of them as "weak" to break the cycle. The one that is chosen affects the elaboration order. Clearly, the user needs a way to control this order, and "weak with" is that feature. In other words, how is the compiler to know *which* with's meet the criteria? I don't think the usual army of elaboration-control pragmas can help. Pragma Elaborate(X) should probably be illegal for weak withs. Anyway, it only goes one step, which as we know from Ada 83 is not good enough. Pragma Elaborate_All should ignore weak withs that are found in the chain. Otherwise, it can't work when used in part of the cycle. Lots of other languages do as Robert suggests -- just allow cyclic imports, with no indication of which ones are "weak". The reason they can get away with it, and we can't is: Some such languages don't have run-time elaboration semantics, so there's no issue. Other such languages have botched the run-time elaboration semantics so badly that this issue is in the noise. > I am not so sure I agree with this. At one level of abstraction you WITH > something because you need something in it, and it is not really your > business if it needs something in yourself. I agree with Pascal on this point. It seems to me that a mutually recursive design always requires *some* mutually recursive knowledge on the part of the designer. I don't see it happening by accident. At least not unless the packages involved are incoherent collections of unrelated stuff. Normal layered "with"s have the property that packages don't need to know about their clients. Mutual recursion seems rather different to me: two packages need to know about each other -- they at least need to know about each other's *existence*. So I think weak with's deserve their own syntax. I guess I agree with Robert that "weak with" is not the best syntax, but that's a minor detail. Using "with" as a verb is already an annoyance... Oh, well. ************************************************************** From: Robert Dewar Sent: Monday, February 3, 2003 8:43 AM > No, I don't think so. The problem is that if there's a cycle of with's, > the compiler needs to choose *one* of them as "weak" to break the > cycle. The one that is chosen affects the elaboration order. Clearly, > the user needs a way to control this order, and "weak with" is that > feature. > > In other words, how is the compiler to know *which* with's meet the > criteria? Yes, that makes sense, so that's not a possibility that is worth discussing. I withdraw the solution. ************************************************************** From: Pascal Leroy Sent: Monday, February 3, 2003 8:37 AM > The only thing needed in the RM is the syntax for weak with's, make sure > the rule about no cycles applies only to cycles formed by strong with's. > > All the stuff below is really a description of how the Rational compiler > would work (and others with a similar library model). There's no need > for any of it in the RM. That statement surprises me. At the very least, it would seem that the RM would need to specify what names/entities are available after a unit has been superficially entered into the environment, and what are the properties of these entities. I am proposing that you can see only packages and types, and even so the types would look like they are incomplete. I can't see how you could deduce this from the current RM. PS: But then I know that Bob Duff, like the Pope, is infallible in matters of language doctrine ;-) ************************************************************** From: Robert A. Duff Sent: Monday, February 3, 2003 8:51 AM Yes, you're right -- the RM needs to say *something*. But it's not much. And I don't think it needs to define this concept of "superficially in the environment" -- that concept belongs in the Rational user's manual, I think. The RM should say something about how if the only visibility to package X is via "weak with", then uses of X are restricted along the lines you say above. More thought is needed, if we are to go this route. The only real problem I see is implementation difficulty. I think all the language rules can be worked out, but that will require some thought. Interactions with elaboration-control pragmas. What happens when you've got both a weak and a strong with on the same thing? Does "weak with A.B.C;" import all three weakly? Is there any concern that changing "with" to "weak with", or vice versa, would introduce surprises into the program? I suspect all these questions have easy straightforward answers. > PS: But then I know that Bob Duff, like the Pope, is infallible in matters of > language doctrine ;-) ;-) No way! ************************************************************** From: Robert A. Duff Sent: Monday, February 3, 2003 12:56 PM In Pascal's proposal, a parent package P can weakly 'with' its children: "weak with P.C;", if you want a mutually dependent pair of types, one in P and one in P.C. The spec of P will be elaborated before the spec of P.C, as usual. However, it is pointless for P.C to say "weak with P;", because the child-to-parent dependency is necessarily strong (given that there's no proposal to add syntax saying otherwise -- "weak children?"). No problems here. I'm just making some observations. I guess I favor Robert's proposed "limited with" syntax. ************************************************************** From: Robert Eachus Sent: Monday, February 3, 2003 2:26 PM So do I, and I am beginning to like this proposal. As far as I can see it is clearly better than package abstracts in two ways, there is no new compilation unit type, and there is no problem of keeping the abstract consistant with the package itself. In other words from a theoretical point of view, the "limited with" creates a package abstract and then withs it. Since the completion of the types and the subprograms (and parameters) get lost in the process, there is no visible circularity. But I think that there are two "gotchas" that have to be dealt with. The first is where you have a type declaration that depends on another type: type Color is (Red, Green, Blue); type Properties(C: Color) is...; Does the incomplete type for Properties include the discriminant? Choices are yes, and it is illegal to do anything with it, type Color is not incomplete, and the type Properties has undefined discriminants. (Others?) I prefer the choice where only private, record and tagged types are incomplete in the limited view. This brings me directly to the second gotcha. I think it is clear (at least to me) that for this to be useful, the limited view of an access type has to be an access type. But there have to be limits on how this access type can be used in the limited view. (Translation, if you create a type with a component of (a limited view of) an access type, then it is illegal to have a non-null default value for a component of that type. Other rules are possible.) ************************************************************** From: Robert A. Duff Sent: Monday, February 3, 2003 2:26 PM > This brings me directly to the second gotcha. I think it is clear (at > least to me) that for this to be useful, the limited view of an access > type has to be an access type. But there have to be limits on how this > access type can be used in the limited view. Can't we say that it's an access type whose designated type is incomplete? And then you can't do things like X.all. >... (Translation, if you > create a type with a component of (a limited view of) an access type, > then it is illegal to have a non-null default value for a component of > that type. Other rules are possible.) I think you can't have an allocator (as a default or anything else), but I don't see why it couldn't be a function call that happens to return the access type. Perhaps an example? ************************************************************** From: Dan Eilers Sent: Monday, February 3, 2003 3:04 PM I like Pascal's "weak with" proposal a lot, although I'm not yet convinced we need to syntactically distinguish "weak with" from normal withs. I came to this conclusion by perhaps a slightly different route: 1) we have a bunch of proposed syntax extensions to solve the circular types problem, any of which are believed to be feasible to implement; 2) essentially none of the proposed syntax extensions have any dual-use benefit towards the solution of any other problem; 3) we envision a tool that can automatically translate circular specs written in other languages into the proposed new syntax; 4) a similar tool could also translate Ada that allowed circular with's into any of the proposed syntax extensions; 5) given such a tool, the users would happily write Ada with circular with's, and use the tool to create their package abstracts (in whatever syntax); 6) this could be made automatic. When the compiler sees a "with" clause for a unit that has not been compiled, instead of giving an error, it can automatically invoke the translation tool to create the package abstract, and then compile the abstract before continuing to compile the original unit. 6) this can be further optimized if desired so the translation tool is integrated into the compiler, rather than needing to be a stand-alone tool. Notes: Recursive subprogram calls don't use different syntax than non-recursive ones, so ideally recursive with's wouldn't either. We need to address the elaboration order concerns, but presumably that is more naturally done with elaboration pragmas than with different syntax for "with" clauses. I still think there is some possible dual-use synergy with the separate private issue. As Tuck corrected me, you can't just put the private part in a separate file. So instead of "private is separate" you would have to say "the remainder of this package is separate". A "with" clause on such a package would continue to see both the root and the subunit. A "with p'abstract" would see only the root. I believe such new syntax would at the same time solve the separate private issue and the circular types issue, and perhaps also the separate constants issue. ************************************************************** From: Robert Dewar Sent: Monday, February 3, 2003 4:07 PM > 6) this could be made automatic. When the compiler sees a "with" clause > for a unit that has not been compiled, instead of giving an error, it > can automatically invoke the translation tool to create the package > abstract, and then compile the abstract before continuing to compile > the original unit. That won't do at all for a source based compilation system, there is no concept of "a unit that has not been compiled". One of the critical guarantees in GNAt is that order of compilation NEVER affects the results and we would strenuously object to a change in semantics which would destroy this guarantee. For one thing it means that the meaning of a program is not fully conveyed by its sources, and that is pragmatically and philsophically unacceptable. ************************************************************** From: Robert A. Duff Sent: Monday, February 3, 2003 4:16 PM Yes, I agree. This makes me think that these tools that translate from other languages (or create bindings thereto, I guess) ought to make sure that their generated Ada code doesn't do anything at elab time -- at least in the presence of cycles. That seems achievable, and simpler than having user options to specify elab order. ************************************************************** From: Robert Dewar Sent: Monday, February 3, 2003 4:25 PM Note that another objection is that this means that automated tools for determining the compilation order would have to do semantic analysis. ************************************************************** From: Robert A. Duff Sent: Monday, February 3, 2003 4:12 PM Dan Eilers wrote: >... > 3) we envision a tool that can automatically translate circular specs > written in other languages into the proposed new syntax; Right, but those languages typically don't have the same elaboration-order issues that Ada does. > 4) a similar tool could also translate Ada that allowed circular with's > into any of the proposed syntax extensions; True, but... > 5) given such a tool, the users would happily write Ada with circular > with's, and use the tool to create their package abstracts (in > whatever syntax); I fear that such a tool would be inherently error prone. Suppose we have package specs A and B, which 'with' each other. The tool would have to choose which one to elaborate first, and it might guess wrong. I think it's better to let the user specify the order via "limited with". Besides, now that I think about it, how would this tool work? "Limited with" implies a very restricted usage of the imported package. The tool would have to check that such restrictions are obeyed before turning a "with" into a "limited with". > 6) this could be made automatic. When the compiler sees a "with" clause > for a unit that has not been compiled, instead of giving an error, it > can automatically invoke the translation tool to create the package > abstract, and then compile the abstract before continuing to compile > the original unit. Users normally don't compile. They run a build tool, which automatically compiles everything that needs to be compiled, in some order that is not easily known to the programmer. It seems error-prone to have the semantics depending on what happens to have already been compiled. That's true even if the user bypasses the build tool and explicitly invokes the compiler in some order. > 6) this can be further optimized if desired so the translation tool is > integrated into the compiler, rather than needing to be a stand-alone > tool. > > > Notes: > > Recursive subprogram calls don't use different syntax than non-recursive > ones, so ideally recursive with's wouldn't either. True, but this case seems different to me. If I see: with B; package A is ... I currently know for certain that the spec of B is elaborated before A. I don't have to go looking at B to see that that's true. I don't think we should break that property. > We need to address the elaboration order concerns, but presumably that > is more naturally done with elaboration pragmas than with different > syntax for "with" clauses. I don't think the existing pragmas can be used for this purpose. Suppose the cycle is "A with's B with's C with's A". Adding a pragma Elaborate in A pointing to B doesn't work, because it's not transitive to C. Adding Elaborate_All doesn't work, because that would require A to be elaborated before itself (because it's transitive). So we would have to add a new pragma to explicitly break one link in the cycle: with X; pragma Don't_Elaborate(X); -- ;-) But that means precisely what "limited with X;" would mean. Elaboration pragmas were a mistake in the first place. They were added late in the game to Ada 83 to solve the "Brosgol anomalies". We added more pragmas in Ada 95, and I'm afraid we botched the job -- we *still* didn't solve the problems. I suggest we avoid compounding the mistake of using pragmas to control elaboration. Furthermore, "limited with X" implies various restrictions on the use of X -- you can only refer to types in X, and they are all incomplete (or access to incomplete). Pragmas shouldn't be in *that* business. > I still think there is some possible dual-use synergy with the separate > private issue. As Tuck corrected me, you can't just put the private > part in a separate file. So instead of "private is separate" you would > have to say "the remainder of this package is separate". A "with" clause > on such a package would continue to see both the root and the subunit. > A "with p'abstract" would see only the root. I believe such new syntax > would at the same time solve the separate private issue and the circular > types issue, and perhaps also the separate constants issue. Perhaps, but I skept. I'm having trouble seeing the separate-private issue as anything more than a simple textual translation, with no run-time consequences whatsoever. I haven't seen a fully worked-out proposal that solves both problems... P.S. This discussion makes me think of another observation. If a given cycle contains two or more "limited with"s, then elaboration order will presumably be implementation defined. I suppose that's OK -- if you care, you can break the cycle with just one "limited with", and use normal "with" for the rest. ************************************************************** From: Dan Eilers Sent: Monday, February 3, 2003 6:23 PM Bob Duff wrote: > Perhaps, but I skept. I'm having trouble seeing the separate-private > issue as anything more than a simple textual translation, with no > run-time consequences whatsoever. I haven't seen a fully worked-out > proposal that solves both problems... OK, here's the canonical example worked out: -- Note: with'ing package P creates a semantic dependency on all subunits -- such as P.Extension declared in P, and makes all declarations in -- P.Extension visible as if they were declared directly in P. -- with'ing P'abstract does not create a semantic dependency on subunits -- such as P.Extension, and does not make p.Extension's declarations visible, -- and does not allow declaring objects of types from P whose full type is -- deferred to P.Extension. package Employees is type Employee is private; -- completed in subunit type Emp_Ptr is access all Employee; package Extension is separate; end Employees; package Departments is type Department is private; type Dept_Ptr is access all Department; package Extension is separate; end Departments; with Departments'abstract; separate(Employees) package Extension is 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; private type Employee is record dept: Departments.Dept_Ptr; end record; end Extension; with Employees'abstract; separate(Departments) package Extension is type Dept_Ptr is access all Department; procedure Choose_Manager(D : access Department; Manager : access Employees.Employee); private type Department is mgr: Employees.Emp_Ptr; end record; end Extension; For the "private is separate" problem, instead of: package p is ... private is separate; end P; you would have: package p is ... private package extension is separate; end; ************************************************************** From: Tucker Taft Sent: Monday, February 3, 2003 4:27 PM Robert A Duff wrote: > > > This brings me directly to the second gotcha. I think it is clear (at > > least to me) that for this to be useful, the limited view of an access > > type has to be an access type. But there have to be limits on how this > > access type can be used in the limited view. > > Can't we say that it's an access type whose designated type is > incomplete? And then you can't do things like X.all. You are starting to give me the "willies." We will have to process rep-clauses if the access type in a limited-with-ed package is not itself considered an incomplete type. If we want a non-incomplete access type, then I think we need to declare it in a non-limited-withed place. Hence, this doesn't solve the problem associated with proliferation of access type declarations. And I do still like that "type C.T;" ;-). (what a pain I can be...) > >... (Translation, if you > > create a type with a component of (a limited view of) an access type, > > then it is illegal to have a non-null default value for a component of > > that type. Other rules are possible.) > > I think you can't have an allocator (as a default or anything else), > but I don't see why it couldn't be a function call that happens to > return the access type. Perhaps an example? Gack. You are back to requiring that the limited-with-er chooses a representation for a type that it isn't declaring. In this era where we are on the cusp of moving from 32-bit pointers to 64-bit pointers, I fear that rep clauses on access types may be more common, rather than less. By the way, I have discovered (remembered?) that we have a "loose parser" built into our compiler. It's job is to do lookahead where strict one-pass semantic analysis is difficult. For example, it scans bodies looking for labels, and then inserts them at the "begin." It scans generic formal parts looking ahead until it finds out in what parent unit the generic is declared, so it can interpret the names in the formal part properly. In any case, this is a full, but very stupid and quite "lax" parser. It could probably build up a tree of package and type names. It could certainly not make any semantic sense out of a rep clause, and since the rep clause could use named numbers declared in other packages (e.g. "for My_Acc_Type'Size use Targ_Dep.Size_Of_Access_Type;"), it really couldn't be made to work. So if we consider this limited-with approach, I think we want to make minimal assumptions about syntactic processing, and zero assumptions about representation processing, of the items in the limited-withed unit. I also agree with Bob that the less said about the way the magic happens the better. Clearly there will be some kind of compilation dependence on the source of the limited-withed unit, but the key point is there is no semantic or elaboration dependence on the unit. It seems fine if the source of the unit is edited "after" something that mentioned it in a limited-with clause is compiled, but before the unit itself is compiled. But of course, whenever the source is changed, everything that has a compilation-dependence on it will probably need to be recompiled prior to linking. The "trick" that makes it all possible is that the *fully compiled* representation of both units in a cyclic dependence depend on the *source text* of both units. The fully compiled representations don't both depend on the others fully compiled representation. Of course, there may be intermediate forms between source text and fully compiled representations, but those don't need to be mentioned. And the other important point is there is no transitive compilation dependence if the unit mentioned in a limited-with clause itself depends on other units, via withs or limited-withs. Presumably, mutual inlining, if supported, requires similar tricks. Other observations if we consider limited-with: I suppose to be consistent, we should interpret "limited with P.Q.R;" as equivalent to "limited with P, P.Q, P.Q.R;". Also it seems clear that limited with is ignored if there is a non-limited with for the same unit. Another important point is that if a unit is mentioned in a limited with clause, it is a *post*-compilation rule that the unit be semantically (or even syntactically) correct, as opposed to a legality rule. When compiling the unit having the limited with, the compiler may of course check some part of this post-compilation rule early (provided of course it doesn't create an inappropriate semantic dependence), but an ACATS test should not require that any particular errors in the unit mentioned in the limited-with be identified at this time. Certainly compilers will vary in how picky is the parser that they use to implement limited with, especially given that Ada's "syntax" rules slop over into semantic processing fairly frequently, due to the lack of direct LALR(1) parsability. The only requirement should be that *if* the unit is semantically correct, then you can safely mention it in a limited-with. But you cannot rely on the compiler detecting all (or any) errors in a unit mentioned in a limited-with, though it can be arbitrarily picky about it. Eventually, prior to linking, it will require that the unit exist and be semantically correct. This means that when a unit mentions a second unit in a limited-with clause, the second unit is "needed by" the first unit, in the sense of 10.2(2-6). The above is related to 10.1.4(5) which says: When a compilation unit is compiled, all compilation units upon which it depends semantically shall already exist in the environment... We could change this to "... upon which it depends semantically or which it mentions in a limited_with_clause shall ..." and it would still be OK. However, 10.1.4(6) which says: The implementation may require that a compilation unit be legal before inserting it into the environment. is too strict. It could be changed to: The implementation may require that a compilation unit satisfy the Syntax Rules before inserting it into the environment. The implementation may require that a compilation unit be legal before allowing it to be mentioned in a with_clause other than a limited_with_clause. ************************************************************** From: Robert A Duff Sent: Monday, February 3, 2003 5:17 PM Tucker wrote: > Robert A Duff wrote: > > Can't we say that it's an access type whose designated type is > > incomplete? And then you can't do things like X.all. > > You are starting to give me the "willies." > [...description of willies] OK, I withdraw my suggestion. But it seems like whatever solution to the mutual recursion problem we choose, we really need to solve the problem of proliferation of silly type conversions. Otherwise, these proposals will be nearly unusable. That is, there ought to be an *implicit* conversion between access types in the "safe" cases. I know we discussed that before. Is it part of any of these proposals, or is it a separate AI? I seem to recall that it might be wrapped up with other access-type issues, like allowing "not null" constraints, and access-to-constant parameters. > And I do still like that "type C.T;" ;-). > (what a pain I can be...) That's a fine solution, IMHO. Your earlier e-mail about mutually-recursive types being part of the same abstraction makes me comfortable with the idea of tying the solution to child packages. (See, it *is* possible to convey sensible information about purely aesthetic concerns. ;-)) But I would choose the "limited with" idea, unless it is considered too hard to implement. > Presumably, mutual inlining, if supported, requires similar tricks. And mutual generic instantiation? > Other observations if we consider limited-with: > > I suppose to be consistent, we should interpret "limited with P.Q.R;" > as equivalent to "limited with P, P.Q, P.Q.R;". Also it seems clear > that limited with is ignored if there is a non-limited with for the > same unit. Or any other "strong" dependence, such as child upon parent. I guess "strong dependence", as I've been calling it, is exactly the same notion as "semantic dependence". > Another important point is that if a unit is mentioned in a limited with > clause, it is a *post*-compilation rule that the unit be semantically > (or even syntactically) correct, as opposed to a legality rule. ... I don't see the need for any verbiage in the RM about this point. And I don't see how this differs (in theory) from normal with_clauses. (It might differ in practise, depending on how the compiler actually works.) I mean, if B says "with A;", there is currently no rule saying "A shall be legal." A compiler could properly say nothing more than "no errors found in B", even if there are errors in A. (That would be unfriendly, IMHO.) Of course, sometime before running the program, A must be compiled, and the errors detected. > ...This > means that when a unit mentions a second unit in a limited-with > clause, the second unit is "needed by" the first unit, in the > sense of 10.2(2-6). Yes, that's a key point. It might be that the second unit is mentioned *only* in limited_with_clauses, in which case there are no elaboration dependendences upon it, but it still needs to be included in the program, and elaborated at some point (possibly very late). > The above is related to 10.1.4(5) which says: > > When a compilation unit is compiled, all compilation units upon which > it depends semantically shall already exist in the environment... > > We could change this to "... upon which it depends semantically or > which it mentions in a limited_with_clause shall ..." and it would still be OK. I don't see any need to change this, given that "existing in the env" is such a vague concept. In GNAT, it just means the file is sitting there on the disk. In AdaMagic, it just means it is sitting there on the disk and has been "registered". Registration only does minimal syntax checking. In Rational's compiler, it means something more. > However, 10.1.4(6) which says: > > The implementation may require that a compilation unit be legal before > inserting it into the environment. > > is too strict. It could be changed to: I don't see why. The GNAT model does not take advantage of this permission. The permission is there for the benefit of compilers like Rational, where "inserting" involves running the compiler, and therefore checking various rules. I suppose AdaMagic takes partial advantage of this permission, since insertion involves some small amount of syntax checking. But why should we care how much checking is done on B before "limited with B" can be swallowed by the compiler? If B is illegal, the compiler can process "limited with B" if it likes, or (if it happens to notice the illegality), it can complain that "limited with B" is nonsense. It doesn't matter whether the illegality is syntactic or semantic. > The implementation may require that a compilation unit satisfy the Syntax > Rules before inserting it into the environment. The implementation may > require that a compilation unit be legal before allowing it to be mentioned > in a with_clause other than a limited_with_clause. I see no need for this added complexity. And it could be a burden -- if the compiler chooses to implement some simple Legality Rule in the parser, that should be allowed. ************************************************************** From: Tucker Taft Sent: Monday, February 3, 2003 6:20 PM Robert A Duff wrote: > Tucker wrote: > ... >>The above is related to 10.1.4(5) which says: >> >> When a compilation unit is compiled, all compilation units upon which >> it depends semantically shall already exist in the environment... >> >>We could change this to "... upon which it depends semantically or >>which it mentions in a limited_with_clause shall ..." and it would still be OK. > > I don't see any need to change this, given that "existing in the env" is > such a vague concept. In GNAT, it just means the file is sitting there > on the disk. In AdaMagic, it just means it is sitting there on the disk > and has been "registered". Registration only does minimal syntax > checking. In Rational's compiler, it means something more. I think we need to say *something* about the requirements on a unit mentioned in a limited-with clause. Why not say it must exist in the environment? As you said, that is pretty vague. If there are no requirements on it, then the implementation has no place to "hang" whatever requirements it really does have. It can't just invent requirements that don't exist. But it can say, "as far as our implementation is concerned, that unit doesn't exist in the environment, so we refuse to compile the limited-with for it." >>However, 10.1.4(6) which says: >> >> The implementation may require that a compilation unit be legal before >> inserting it into the environment. >> >>is too strict. It could be changed to: > > I don't see why. Again, we need a place for the implementation to "hang" its requirements. We have given them the first handle, by allowing them to require existence. Now we are giving them the second handle, allowing them to impose certain other requirements on the unit. They *clearly* can't impose complete legality requirements, if the units are inserted one at a time, since there is no order of insertion that would allow that. > ... > The GNAT model does not take advantage of this > permission. The permission is there for the benefit of compilers like > Rational, where "inserting" involves running the compiler, and therefore > checking various rules. I suppose AdaMagic takes partial advantage of > this permission, since insertion involves some small amount of syntax > checking. > > But why should we care how much checking is done on B before "limited > with B" can be swallowed by the compiler? If B is illegal, the compiler > can process "limited with B" if it likes, or (if it happens to notice > the illegality), it can complain that "limited with B" is nonsense. > It doesn't matter whether the illegality is syntactic or semantic. You are missing the point. I am simply *allowing* compilers to impose requirements, but only up to a point. Without such verbiage, I don't believe compilers would be allowed to impose any requirements. I suppose you could argue all of this verbiage is unnecessary already, but since we felt the need to put it there in the first place, and we presumably aren't going to take it out, I believe it needs to be updated to *allow* compilers to impose certain requirements on limited-with'ed units. But I definitely don't want to force them to impose these requirements (and I don't think my suggested wording does that). >> The implementation may require that a compilation unit satisfy the Syntax >> Rules before inserting it into the environment. The implementation may >> require that a compilation unit be legal before allowing it to be mentioned >> in a with_clause other than a limited_with_clause. > > I see no need for this added complexity. And it could be a burden -- if > the compiler chooses to implement some simple Legality Rule in the > parser, that should be allowed. I'm not sure what we are discussing any more, since you don't seem to think that the unit even need to exist in the environment to be mentioned in the limited-with clause. I think we need to *allow* implementations to impose some requirements, and we might as well tie it to the notion of "exist" in the environment. *If* we tie it to that notion, we want to be sure that limited-with can in fact be used. That seems to mean that you must be able to insert units into the environment before they can be (fully) compiled, strictly for the purposes of doing a "limited with" of them. Why don't you suggest some wording so we actually have an alternative to consider? ************************************************************** From: Robert A Duff Sent: Monday, February 3, 2003 6:46 PM Tucker wrote: > I think we need to say *something* about the requirements > on a unit mentioned in a limited-with clause. Why not say > it must exist in the environment? As you said, that is > pretty vague. If there are no requirements on it, then > the implementation has no place to "hang" whatever requirements > it really does have. It can't just invent requirements that > don't exist. But it can say, "as far as our implementation > is concerned, that unit doesn't exist in the environment, > so we refuse to compile the limited-with for it." OK, you have convinced me on that point. But as to 10.1.4(6): > Again, we need a place for the implementation to "hang" > its requirements. We have given them the first handle, > by allowing them to require existence. Now we are > giving them the second handle, allowing them to impose > certain other requirements on the unit. But 10.1.4(6) as is already gives sufficient permission. Your proposed change gives *less* permission. I claim there is no need to be more restrictive (on implementations), and that being more restrictive requires more complex RM wording. 10.1.4(6) as is allows compilers to refuse to deal with "limited with X" on an illegal X. That is sufficient permission. If X is in the env, and is legal, the compiler must deal with "limited with X", because the only permission to refuse to admit X into the env is if it's illegal. > They *clearly* > can't impose complete legality requirements, if the > units are inserted one at a time, since there is no > order of insertion that would allow that. There is no requirement that units are inserted one at a time (and in fact they are not, in AdaMagic). Hence, it is not "*clearly*" true that they can't impose legality requirements. Your wording for 10.1.4(6) does not allow a compiler to impose such legality requirements. I think that's overspecification, and could be an implementation burden. For example, it is reasonable to implement some legality rules in the parser, and some compilers do that. One might wish to run the parser on X when seeing "limited with X". Your wording would disallow such an implementation. Imagine a front end structured as three phases: 1. Parse and build syntax tree. 2. Walk tree, build symbol table nodes for explicit declarations, collect a list of all type decls, and check *some* legality rules. 3. Overload resolution, and check the rest of the legality rules. The "limited with" wants that list of type decls from phase 2. But your wording seems to forbid phase 2 from printing any error messages, because they're not syntax errors. > I'm not sure what we are discussing any more, since you > don't seem to think that the unit even need to exist > in the environment to be mentioned in the limited-with > clause. I backed off on that part. >... I think we need to *allow* implementations > to impose some requirements, and we might as well tie > it to the notion of "exist" in the environment. > *If* we tie it to that notion, we want to be sure that > limited-with can in fact be used. That seems to mean > that you must be able to insert units into the environment > before they can be (fully) compiled, strictly for the purposes > of doing a "limited with" of them. > > Why don't you suggest some wording so we actually > have an alternative to consider? OK, I propose changing 10.1.4(5) in the way you suggested, and leaving 10.1.4(6) as is. ************************************************************** From: Tucker Taft Sent: Monday, February 3, 2003 7:02 PM Robert A Duff wrote: > ... > OK, I propose changing 10.1.4(5) in the way you suggested, and leaving > 10.1.4(6) as is. I suppose there is an unwritten rule that there must be some way for two packages with cylic dependence to be inserted into the environment. Clearly if the "insertion" process requires legality, and you can only insert one unit at a time, there is a problem. Either the compiler must allow simultaneous multiple-file insertion (that sounds like it might be illegal in Nebraska ;-), or it must allow units that are not yet demonstrably legal to be inserted. ************************************************************** From: Robert A Duff Sent: Monday, February 3, 2003 7:14 PM Don't you think it's OK to leave that rule unwritten? I mean if that's a hole, it's already a hole. If I have some legal code, I must be able to insert it into the env *somehow*, since there's no permission for the implementation to refuse legal code. The fact that the implementation is incapable of determining whether it's legal seems irrelevant. *I* know it's legal. I think you're worrying overmuch about implementation issues (in considering RM wording). Legal code must be accepted. Therefore, code must be accepted if the compiler does not (yet) know whether it's legal. The compiler can only refuse if it can *prove* it's illegal. Make sense? ************************************************************** From: Tucker Taft Sent: Monday, February 3, 2003 8:33 PM After thinking more about this, I think we need to make some change to the wording of 10.1.4(6). It really doesn't make any sense if we change 10.1.4(5) as proposed. We seem to agree that this sentence is primarily for non-source-based compilers, where the only way to insert a unit into the environment is by processing it in some way. But it is exactly those compilers that will have to change to support limited-with. They *cannot* require legality of all units inserted into the environment, if we have just changed 10.1.4(5) to require that the unit mentioned in a limited-with must already *be* in the environment. I think we should say something closer to this: Before inserting a compilation unit into the environment, the implementation may require that it obey all the Syntax Rules, and any other rules that it can check given what other units already exist in the environment. > ... > I think you're worrying overmuch about implementation issues (in > considering RM wording). Legal code must be accepted. Therefore, code > must be accepted if the compiler does not (yet) know whether it's legal. > The compiler can only refuse if it can *prove* it's illegal. Make > sense? No. ;-). I think either we should make 10.1.4(6) completely implementation-defined, or replace it with something that reflects the problem that non-source-based compilers will have with limited withs, since we know that 10.1.4(6) was crafted specifically for their needs. Remember, this is in the section called "implementation permissions" so it is specifically talking to implementors, not users, and it is specifically talking about implementation issues, not abstract semantic issues. ************************************************************** From: Robert A Duff Sent: Monday, February 3, 2003 7:06 PM Pascal, This "weak with" ("limited with") thing is your proposal, but if you like, I might be willing to work out the RM wording tomorrow (Tuesday). This idea seems to be gaining favor, and if it's to be chosen, I don't really want that to take 17 more ARG meetings. Producing wording now might speed things up. On the other hand, if folks think it's too hard to implement, please say so before I waste my time. Should I do it? ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 3:21 AM It would be very useful if you did that. You are much more competent than me at crafting RM wording, and furthermore I don't think I would have time to do that between now and the meeting. Since there seems to be some interest for the idea, it would be good to have a precise proposal on the table to discuss it. Experience has proven that discussing an AI without an initial write-up is sterile. ************************************************************** From: Randy Brukardt Sent: Monday, February 3, 2003 8:35 PM I'm not Pascal, but I'd suggest going ahead and writing up the wording. I'm having a hard time getting a handle on this proposal, and I'm sure that would help. OTOH, since it is a complete start from scratch, it's going to take 17 meetings to get it right in any case. At most, you can reduce that number by one. :-) > On the other hand, if folks think it's too hard to implement, please say > so before I waste my time. At the moment, I think it's too hard to implement (given that type stubs are expected to take 4 hours of work; this would be more like 4 weeks). But that may be irrelevant. More important is that I don't see any way, within the current design of Janus/Ada, to implement something like this. The problem is with the obsoleteness check. Janus/Ada uses a serial number created from the time stamp of the symbol table file created for a library unit for checking. Also, the Janus/Ada compiler knows nothing about source files other than the one it is compiling at the moment. (Other tools are a bit smarter, but the compiler does not use that information itself.) It only knows about symbol table files. One presumes that you'd have a new kind of symbol table file that held a 'lightly' compiled package spec for limited withs. And you'd use the time stamp of that file to create the serial number for obsoleteness checking. Clearly, when you 'really' compiled the spec, the new kind of package spec would have to be updated (otherwise, it wouldn't necessarily match the source file, because the source file could have been edited). But that would necessarily change the serial number, and then the links would fail. And there would be no way to avoid the problem. Given that there is no reliable way to determine if a file has been changed (file time stamps are too crude, often changing when nothing actually is changed, and aren't kept to enough accuracy on Windows), I don't see any way to implement this. The compiled=changed model works fine for Ada 95, but is a complete failure for limited with. Essentially, you have to adopt a source-based model, and that is a major change. This also has a giantic impact on build tools. They'd have to be able to process this new clause and come up with an appropriate order in the face of cycles. Of course, tools just punt if that happens now. It may be I just haven't thought about it enough. But I have a lot of things that I need to do before I leave for the meeting, and thinking about this isn't high on the list. ************************************************************** From: Tucker Taft Sent: Monday, February 3, 2003 8:53 PM Randy's comments make me notice something a bit unfortunate about the limited-with proposal. Although we don't need a new kind of compilation unit, we do need a new way of compiling a unit in the non-source-based environment. (I'll call this new way of compiling "preregistering" the unit.) Supporting limited-with means that either the programmer preregisters all compilation units on the off chance they might be mentioned in a limited-with, or the compilation-order- determination tool gets smarter and figures out which units need to be "preregistered" and which don't. Having this additional step could have significant ripple effects into "make" scripts, regression testing scripts, ACATS testing scripts, compilation-order- determination tools, etc. It also means a new step to explain to programmers. None of the above is particularly rocket science, but it does add to the implementation and "deployment" cost of this proposal. In general, I think this might emerge as a very elegant solution, possibly as elegant as the package prefix/abstract approach, but significantly more implementation effort than either the "type C.T;" or the possibly modified/restricted type stub proposal. By the way, although it sounds like implementing "type C.T;" in Randy's compiler might be a bit more work than a restricted type stub, it seems like that is small potatoes compared to the heavy lifting involved in the limited-with proposal. ************************************************************** From: Robert Dewar Sent: Monday, February 3, 2003 8:53 PM BY the way, this discussion of what is and is not required of compilers reminds me that I think it would be good to have an annex for the standard that defined a standard interchange format, right down to the bit-level coding and file format. That would for example, partly satisy Dan's concern about incomaptible source representation issues. ************************************************************** From: Gary Dismukes Sent: Tuesday, February 4, 2003 1:28 AM > But it seems like whatever solution to the mutual recursion problem we > choose, we really need to solve the problem of proliferation of silly > type conversions. Otherwise, these proposals will be nearly unusable. I tend to agree. Certainly with the latest "limited with" proposal there are generally going to be multiple access types for the same designated type and that's true for most of the other proposals as well. It seems though that the models using child packages may be less prone to that since the child package could use an access type declared in the parent and reexport using a subtype if needed. > That is, there ought to be an *implicit* conversion between access types > in the "safe" cases. I know we discussed that before. Is it part of > any of these proposals, or is it a separate AI? I seem to recall that > it might be wrapped up with other access-type issues, like allowing "not > null" constraints, and access-to-constant parameters. Perhaps you're thinking of AIs 230 (generalized access types) and 231 (access-to-constant parameters and null-excluding subtypes). AI-230 would allow more things to be declared using anonymous general access types, which would increase the number of contexts where implicit conversions can occur, though I'm not sure how much help that will be for the mutual-dependence cases where there will be multiple distinct access types and pointers of those distinct types floating around. I suppose it will help to some degree. In any case, I think it's probably best not to wrap a requirement for easier conversions in with the AI-217 proposals, as I think that may tend to muddy the discussion (even more more than it already is;). > > And I do still like that "type C.T;" ;-). > > (what a pain I can be...) > > That's a fine solution, IMHO. Your earlier e-mail about > mutually-recursive types being part of the same abstraction > makes me comfortable with the idea of tying the solution to > child packages. (See, it *is* possible to convey sensible information > about purely aesthetic concerns. ;-)) > > But I would choose the "limited with" idea, unless it is considered too > hard to implement. My feeling as well. Tucker's modified child package proposal definitely seems the simplest both in description and implementation effort (well, I guess Randy doesn't agree with that...), but like others I'm somewhat dissatisfied with requiring a child-based model to get mutual recursion. I was finding my mind being drawn back to wishing we could just do things as simply as other languages are able to, but had decided that such an approach would be tilted too much in favor of source-based compilers. But now that Pascal's bravely put forward that very suggestion, I'm happy to line up behind it if it stands up to scrutiny. I think the critical question is how much work that approach will be for non-source-based implementations. ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 4:04 AM > Perhaps you're thinking of AIs 230 (generalized access types) and 231 > (access-to-constant parameters and null-excluding subtypes). AI-230 > would allow more things to be declared using anonymous general access > types, which would increase the number of contexts where implicit > conversions can occur, though I'm not sure how much help that will be > for the mutual-dependence cases where there will be multiple distinct > access types and pointers of those distinct types floating around. > I suppose it will help to some degree. AI 230 looked quite promising, but it has been on Tuck's action item list for about two years, and he didn't do his homework (but then he'll have some time on the plane, so you never know...) > In any case, I think it's > probably best not to wrap a requirement for easier conversions in with > the AI-217 proposals, as I think that may tend to muddy the discussion > (even more more than it already is;). Agreed. ************************************************************** From: Robert A Duff Sent: Tuesday, February 4, 2003 4:11 PM I don't want to muddy the discussion *too* much. But many (if not all) of the cyclic-dependency solutions are going to introduce extra access type decls. I don't want folks to reject proposals for that reason. Therefore, we need to at least keep an open mind -- assume that the access-type-conversion annoyance can be solved. ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 3:02 AM > In Pascal's proposal, a parent package P can weakly 'with' its children: > "weak with P.C;", if you want a mutually dependent pair of types, > one in P and one in P.C. The spec of P will be elaborated before the > spec of P.C, as usual. > > However, it is pointless for P.C to say "weak with P;", because the > child-to-parent dependency is necessarily strong (given that there's no > proposal to add syntax saying otherwise -- "weak children?"). > > No problems here. I'm just making some observations. In my proposal it would also be possible for unit P to say "limited with P", but I think we would want to forbid that, as it seems methodologically dubious. (If you follow the model to its logical conclusions, it would for instance allow forward references to packages and types within the spec of P.) ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 3:21 AM > The first is where you have a type declaration that depends on another > type: > > type Color is (Red, Green, Blue); > type Properties(C: Color) is...; > > Does the incomplete type for Properties include the discriminant? I have no doubt that the incomplete type for Properties doesn't include the discriminant. We discussed that issue when we did "with type" and we concluded that any other choice was causing endless trouble. Discriminants of incomplete types are not too useful anyway, they can only be used in building contrained access-to-object types. And the added complexity is not worth it. > I prefer the choice where only private, record and tagged > types are incomplete in the limited view. But then you would need to do a lot of semantic analysis, and you would have to define very precisely what happens during a "superficial" compilation. Consider for instance: type R is record ... end record; type T is new Integer range R'Alignment .. R'Size; How can T be properly defined if R is incomplete? And don't tell me that T would be some new kind of "incomplete integer type", because that would add so much complexity to the language that I'd rather forget about the entire proposal. As Tuck pointed out in his "willies" message, the only way that this model can be made to work is if the first phase can be performed by a brain-dead parser. ************************************************************** From: Robert I. Eachus Sent: Tuesday, February 4, 2003 11:38 AM Pascal Leroy wrote: >I have no doubt that the incomplete type for Properties doesn't include the >discriminant. We discussed that issue when we did "with type" and we concluded >that any other choice was causing endless trouble. About 4 AM, I realized that what is needed is the fact that Properties is a type with discriminants, notthing more. But we already have that concept well defined, and it covers incomplete tagged types. Then I went back to sleep. So I propose that (incomplete) tagged types and types like Properties have unknown discriminants. This allows incomplete discrete types to be treated like other incomplete types, and incomplete array types to also be treated as incomplete with unknown discriminants. >But then you would need to do a lot of semantic analysis, and you would have to >define very precisely what happens during a "superficial" compilation. Consider >for instance: > > type R is record ... end record; > type T is new Integer range R'Alignment .. R'Size; > >How can T be properly defined if R is incomplete? And don't tell me that T >would be some new kind of "incomplete integer type", because that would add so >much complexity to the language that I'd rather forget about the entire >proposal. The nightmare that woke me up was not quite this case, but related. (Using T'First, etc., of another discrete type.Hiding the literals is not enough.) As for access types, I still think we need to special case those. At first I was only worried about the proliferation of access types with the same target. But then I realized that the type matching nightmare gets worse in cases where some of the potential access types are hidden by incompleteness. The messy case is X: T := F.all. F can be an overloaded function, and I would hate to have the legality checked twice, especially if one of the checks depended on elaboration order. There is another painful case though, and I am not sure what to do about it... limited with B; package A is type Foo is record...end record; ... end A; with A; package B is type TA is access A.Foo; type Bar is record TAC: TA; end record; ... end B; (You can produce variations on this theme by creating a third package in the cycle.) We are not talking pathological here, this is part of the core functionality. Now, what is the status of B.TA inside the spec of A? If it is incomplete we get access type proliferation. If it is an access type, is it an incomplete access type? Inquiring minds want to know. If you prefer, write the example as: limited with A.B; package A is type TA is access B.T; ... end A; package A.B is type T is tagged record...end record; ... end A.B; I can live with this version working where the first one breaks, but now we get back to the issue of representation of the access type. Whether it has to be a fat pointer or not will depend on features of T. Again, I can live with that, we just have to determine which features are visible. (Unknown discriminants for T in this case seems minimal.) ************************************************************** From: Tucker Taft Sent: Tuesday, February 4, 2003 2:20 PM "Robert I. Eachus" wrote: > About 4 AM, I realized that what is needed is the fact that Properties > is a type with discriminants, notthing more. Why? >... But we already have that >concept well defined, and it covers incomplete tagged types. Then I went >back to sleep. So I propose that (incomplete) tagged types and types >like Properties have unknown discriminants. This allows incomplete >discrete types to be treated like other incomplete types, and incomplete >array types to also be treated as incomplete with unknown discriminants. Why can't we treat these all as plain old incomplete types? You haven't explained that. >>But then you would need to do a lot of semantic analysis, and you would have to >>define very precisely what happens during a "superficial" compilation. Consider >>for instance: >> >> type R is record ... end record; >> type T is new Integer range R'Alignment .. R'Size; >> >>How can T be properly defined if R is incomplete? And don't tell me that T >>would be some new kind of "incomplete integer type", because that would add so >>much complexity to the language that I'd rather forget about the entire >>proposal. >> >The nightmare that woke me up was not quite this case, but related. > (Using T'First, etc., of another discrete type.Hiding the literals is >not enough.) I am totally lost. These types are incomplete. You can't do anything with them. > As for access types, I still think we need to special case those. At > first I was only worried about the proliferation of access types with > the same target. But then I realized that the type matching nightmare > gets worse in cases where some of the potential access types are hidden > by incompleteness. The messy case is X: T := F.all. F can be an > overloaded function, and I would hate to have the legality checked > twice, especially if one of the checks depended on elaboration order. I must be missing something fundamental here. All of these types are just plain old incomplete types. Yes, I agree we ought to special case tagged and allow a bit more, but that seems irrelevant to what you are talking about. What is messy about the messy case? >... >(You can produce variations on this theme by creating a third package in >the cycle.) We are not talking pathological here, this is part of the >core functionality. Now, what is the status of B.TA inside the spec of >A? If it is incomplete we get access type proliferation. If it is an >access type, is it an incomplete access type? Inquiring minds want to know. It is incomplete and we get access type proliferation. I don't see that "limited with" can solve the access type proliferation problem, and trying to make it do so is doomed, in my mind. ... >I can live with this version working where the first one breaks, but now >we get back to the issue of representation of the access type. Whether >it has to be a fat pointer or not will depend on features of T. Again, >I can live with that, we just have to determine which features are >visible. (Unknown discriminants for T in this case seems minimal.) I am presuming that all non-tagged types are incomplete, period. No new notion of "incomplete access types" is being added. ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 3:00 PM > > So I propose that (incomplete) tagged types and types > > like Properties have unknown discriminants. This allows incomplete > > discrete types to be treated like other incomplete types, and incomplete > > array types to also be treated as incomplete with unknown discriminants. > > Why can't we treat these all as plain old incomplete types? > You haven't explained that. I am as puzzled as Tuck. It is my understanding that unknown discriminants for incomplete types are pretty much equivalent to no discriminants. So it's fine with me to insist that these types have unknown discriminants, but it doesn't make the slightest difference. (Remember, we are talking _incomplete_ types here, so you cannot do much with them anyway.) > I am presuming that all non-tagged types are incomplete, period. No new > notion of "incomplete access types" is being added. Agreed. This seems like the only way to avoid madness. Granted, this proposal doesn't solve the problem of proliferation of access types, but that problem shows up in contexts that are unrelated to circular type dependencies, so I think that it should be addressed by a different mechanism. ************************************************************** From: Randy Brukardt Sent: Tuesday, February 4, 2003 2:53 PM Tucker said, replying to Robert Eachus: > > If it is incomplete we get access type proliferation. If it is an > > access type, is it an incomplete access type? Inquiring > > minds want to know. > > It is incomplete and we get access type proliferation. > > I don't see that "limited with" can solve the access type proliferation > problem, and trying to make it do so is doomed, in my mind. At which point, I lose interest in the proposal. All of type stubs, restricted type stubs, and Tuck's type C.T idea do solve the access type proliferation problem. Admittedly the structure of the declarations is unnatural. But the only real alternative is to adopt something like AI-230. That bulks up the proposal a ton. So, limited with: -- Is much harder to implement in library based compilers. It's probably harder to implement than type stubs/type C.T in source-based compilers as well (certainly no easier). Affects compilation tools in all environments; -- Doesn't solve the access type proliferation problem. The only advantage seems to be: -- A more natural expression of package structure. But that's dubious, since you need to resort to unnatural package structures in order to share access types. So you're pretty much left with "much harder to implement". ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 3:09 PM > > I don't see that "limited with" can solve the access type proliferation > > problem, and trying to make it do so is doomed, in my mind. > > At which point, I lose interest in the proposal. Access type proliferation in and of itself is not a problem. What is a problem is access type conversions. That problem shows up in all sorts of contexts, notably in OOP where you have access types (class-wide or specific) designating some type in a hierarchy, and all conversions must be explicit, even those that cannot possibly fail. That is a big pain. This has nothing to do with circular type dependences. Even if we select a solution to the problem of circular type dependences that doesn't cause proliferation of access types, the problem of access type conversions will remain. In C, if you have a type t you automatically get a pointer type t*. In Ada, I often wish we had a similar capability, where I wouldn't have to declare all these silly access types and convert between them explicitly. ************************************************************** From: Robert I. Eachus Sent: Tuesday, February 4, 2003 5:20 PM Tucker Taft wrote: >It is incomplete and we get access type proliferation. > >I don't see that "limited with" can solve the access type proliferation >problem, and trying to make it do so is doomed, in my mind. And in my mind the proposal is doomed (or not the best one on the table) if it doesn't address the access type proliferation problem. >I am presuming that all non-tagged types are incomplete, period. No new >notion of "incomplete access types" is being added. Hmm. I must not be making myself clear. My idea is not to add "incomplete access types," but to try to make the limited with idea work with access types to these new special incomplete types. The refractory issue is when you have access types that carry descriptor information and when you don't. Where that data is stored is irrelevant. Even if you store the descriptor for an unconstrained array type with the data and pointers to both unconstrained and constrained arrays are the same size, some conversions and assignments are impossible or require recopying the data: package P is type Str is new String; subtype Str4 is Str(1..4); type PT1 is access Str; type PT2 is access Str4; end P; or if you prefer: limited with P; package Q is type PT3 is access P.Str; type PT4 is access P.Str4; end Q; Now I need to compile some code--perhaps the body of Q--that needs to do those damned explicit type conversions, or even just some simple assignments. As a programmer I have a problem, but without more information about the types in P than Tuck seems willing to allow, the compiler is SOL. It doesn't know how to create objects of type PT3 and/or PT4, and even has trouble with legality issues: ... PO3: PT3; PO4: PT4; ... PO3.all := PO4.all; -- legal? Right now the voting seems to be that either the limited with idea is doomed because it is too much work, or it is doomed because it can't deal with the issue of access type proliferation. I'll let sleeping dogs lie. ************************************************************** From: Tucker Taft Sent: Tuesday, February 4, 2003 5:58 PM "Robert I. Eachus" wrote: > > Tucker Taft wrote: > > >It is incomplete and we get access type proliferation. > > > >I don't see that "limited with" can solve the access type proliferation > >problem, and trying to make it do so is doomed, in my mind. > > > And in my mind the proposal is doomed (or not the best one on the table) > if it doesn't address the access type proliferation problem. Well, I think trying to make it address this problem very tricky, since it means looking at the designated subtype indication, and that will get you back to worrying about use visibility, etc. > >I am presuming that all non-tagged types are incomplete, period. No new > >notion of "incomplete access types" is being added. > > > Hmm. I must not be making myself clear. My idea is not to add > "incomplete access types," but to try to make the limited with idea work > with access types to these new special incomplete types. The refractory > issue is when you have access types that carry descriptor information > and when you don't. Where that data is stored is irrelevant. Even if > you store the descriptor for an unconstrained array type with the data > and pointers to both unconstrained and constrained arrays are the same > size, some conversions and assignments are impossible or require > recopying the data: I am getting an inkling of your concern. At least one of the problems you seem to be worrying about is an implementation issue (as opposed to a semantics issue), and is one that bedevils the "unrestricted" type stub proposal, and the "with type" proposal. Namely that when choosing a representation for access types to the full type, the compiler may not be aware that there are access types to the incomplete type. I don't think this is quite as serious as the problem with the "with type" proposal, since the compiler is doing some serious peeking at the full type when it creates the incomplete type, so *if* it has multiple access type representations, it can mark the incomplete type with enough extra information so that it will choose an appropriate representation for any access type that is declared later which "sees" only the incomplete type. Unfortunately, derived types create a bit of a problem, since they don't reveal syntactically what sort of type they are (e.g., whether their first subtype is an unconstrained array subtype and hence pointers to it should be default be "fat" pointers). This would mean that the "peeker" would have to make a "guess" about whether the incomplete type is an unconstrained array. Probably it would assume all derived types are not unconstrained arrays. In any case, whatever guess it makes, it would want to record that in the limited view of the package. When the full view is compiled, as part of the "compatibility" check it would also have to check that the "guess" was correct. If the guess was wrong, and the type is in fact an unconstrained array, say, then it would have to record that fact on the incomplete type, and bump the timestamp (or equivalent) of the limited view of the package. Ultimately the units that depend on this limited view would get recompiled. For a source-based compiler, there isn't really any place to permanently record the "correct" answer, so it would probably end up being a link-time check, and if there were an incompatibility, the offending units would be recompiled with a switch directing them to treat the problematic incomplete type as an unconstrained array, even though it wasn't obvious. > > package P is > type Str is new String; > subtype Str4 is Str(1..4); > type PT1 is access Str; > type PT2 is access Str4; > end P; > > or if you prefer: > > limited with P; > package Q is > type PT3 is access P.Str; > type PT4 is access P.Str4; This wouldn't be legal, since Str4 is defined by a subtype, not a type declaration. > end Q; > > Now I need to compile some code--perhaps the body of Q--that needs to do > those damned explicit type conversions, or even just some simple > assignments. As a programmer I have a problem, but without more > information about the types in P than Tuck seems willing to allow, the > compiler is SOL. It doesn't know how to create objects of type PT3 > and/or PT4, and even has trouble with legality issues: I don't see how legality issues enter into the problem of access type representation. You can't do much of anything with incomplete types. And you can't dereference access-to-incomplete, unless perhaps there is a complete type "nearby". > ... > PO3: PT3; > PO4: PT4; > ... > PO3.all := PO4.all; -- legal? > > Right now the voting seems to be that either the limited with idea is > doomed because it is too much work, or it is doomed because it can't > deal with the issue of access type proliferation. I'll let sleeping > dogs lie. I don't think it has been abandoned, so it is still important to try to identify (real ;-) problems associated with the proposal. ************************************************************** From: Robert I. Eachus Sent: Tuesday, February 4, 2003 6:34 PM Tucker Taft wrote: >Well, I think trying to make it address this problem very tricky, >since it means looking at the designated subtype indication, >and that will get you back to worrying about use visibility, etc. Yep. ... >For a source-based compiler, there isn't really any place to >permanently record the "correct" answer, so it would probably >end up being a link-time check, and if there were an incompatibility, >the offending units would be recompiled with a switch directing >them to treat the problematic incomplete type as an unconstrained array, >even though it wasn't obvious. Yep, it is a problem in any solution to the mutual dependence problem. The issue here is deciding what aggrevations to add--to the user, the implementor, or both. We could make fairly draconian rules that stated that any unit that actually uses a subtype, access type to, or type derived from one of these magic incomplete types must depend on the limited withed unit, and thus see the full type. I think so far this has been an implicit assumption, but the devil is in the details. To take your discussion above a bit further, what happens when you modify the limited withed unit in a way that changes the necessary representation of an access type declared elsewhere? It begins to look like a very real dependence somehow magically wished away. You end up having to recompile some units due to changes in units they don't depend on.... >I don't think it has been abandoned, so it is still important >to try to identify (real ;-) problems associated with the >proposal. > Consider the (dead) horse to have been additionally flogged. ************************************************************** From: Robert A Duff Sent: Tuesday, February 4, 2003 7:06 PM Note that we already have a case where you can create access-to-incomplete without knowing what the complete type looks like. Namely, an incomplete type declared in a package spec, and completed in the body. ************************************************************** From: Robert I. Eachus Sent: Tuesday, February 4, 2003 10:34 PM What did we used to call that? Ah, yes, I remember the Tucker Taft amendment. ;-) The only good thing about it was that the incomplete type had to be private, so it couldn't be seen outside the package. If you weren't following LMC/ARG actions then look up (Ada-83) AI-7. For one meeting, John Goodenough put the AI's for consideration in two books--AI-2 and AI-7, plus related AI's, and everything else. (The final version of AI-00007/19-BI-WJ--yes that is version 19--is actually a consolidation of about a dozen separate AI's) As I recall, AI-7 underwent combinatorial explosion, we discovered lots of nits and crannies, and finally came up with a smoking gun case that did not require the TTA to fire off. But if you think my concern about access to incomplete types with discriminants is overdone, you really should go back and look at all those AI's. As I recall, the final version of AI-7 allowed the legality check on discriminant constraints of access to incomplete objects to be done in one of three places--because there were cases when each of the three wouldn't work, and in some cases more than one. Any "solution" to the interlocking types problem that re-opens that can of worms should be shot then burned and the ashes stirred and buried under a crossroads at midnight. I think net that AI-7 and its relatives took the equivalent of several three-day meetings to resolve. (This was "fixed" in Ada 95 by deferring all compatibility checks until object creation. ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 3:55 AM > At the moment, I think it's too hard to implement (given that type stubs are > expected to take 4 hours of work; this would be more like 4 weeks). But that > may be irrelevant. More important is that I don't see any way, within the > current design of Janus/Ada, to implement something like this. Not sure how you came up with the 4 hours estimate, but I'm sure it would take me more than that to do a detailed design. At this point I'd say that the implementation effort for any of these proposals would be in the range 4 weeks .. 4 months, and I can't be more specific without doing a detailed analysis. But anyway, I don't think that numbers are very interesting at this point. It's really the essence of the implementation difficulties that need to be looked at. As far as I can tell, you and I have the same set of problems, because we are both library-based. I realize that the devil is in the details, and that the magnitude of changes can vary substantially from one implementation to another, but I don't buy that the "limited with" proposal would force you to go to a source-based model. (For Rational, going to a source-based model is a no-no; we'd rather get out of the Ada business; so hopefully there has to be an implementation technique where we stick to our current library model.) > The problem is with the obsoleteness check. Janus/Ada uses a serial number > created from the time stamp of the symbol table file created for a library > unit for checking. Also, the Janus/Ada compiler knows nothing about source > files other than the one it is compiling at the moment. (Other tools are a > bit smarter, but the compiler does not use that information itself.) It only > knows about symbol table files. Fine. If I substitute "Diana tree" for "symbol table", that's essentially true of Apex too. (Ignoring all the incremental compilation crap.) > One presumes that you'd have a new kind of symbol table file that held a > 'lightly' compiled package spec for limited withs. And you'd use the time > stamp of that file to create the serial number for obsoleteness checking. > Clearly, when you 'really' compiled the spec, the new kind of package spec > would have to be updated (otherwise, it wouldn't necessarily match the > source file, because the source file could have been edited). And we would indeed have the same problem. We would first created a "parsed" Diana tree (during "superficial" compilation) and then we would rewrite it to make it "analyzed" (during "full" compilation). Surely that would change the timestamp. > But that would > necessarily change the serial number, and then the links would fail. And > there would be no way to avoid the problem. > > Given that there is no reliable way to determine if a file has been changed The way that I plan to do this is to store in the "parsed" Diana tree (what you call the "light symbol table") a hash code. This hash code could be based on the sequence of tokens in the source, or it could be more clever and be based only on the names of types and packages (the information that is available after "superficial" compilation). Whenever clients currently use a timestamp to check for obsolescence, they would use the combination . If the timestamps match, everything is fine. Otherwise, the Diana trees need to be opened, and the hashes need to be compared. I realize that this will result in some extra open system calls, but Ada's separate compilation already requires a zillion system calls, so I won't lose any sleep on that. Of course, a hash code is not exactly 100% safe, as there could be collisions. But if you design it well enough, you won't see a collision in your lifetime. (We use 96-bit hash codes all over the place for incremental compilation, and we have never run into a collision in all these years.) > This also has a giantic impact on build tools. They'd have to be able to > process this new clause and come up with an appropriate order in the face of > cycles. Of course, tools just punt if that happens now. But the trick is that there are no prerequisites for "superficial" compilation. So one approach is to blindly run "superficial" compilation over all the units, in any order you like (e.g. the order of i-nodes in the filesystem if you like). And then run "full" compilation in an order compatible with normal with clauses and other semantic dependencies, just like you do now (and ignoring "limited with" clauses). No need to process the new clause, no need to change the algorithm that determines the compilation order. Of course you might want to be more clever and avoid the "superficial" phase if it's not needed, but that's an optimization, not something you would have to do for correctness. ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 4:00 AM > Supporting limited-with > means that either the programmer preregisters > all compilation units on the off chance they might > be mentioned in a limited-with, or the compilation-order- > determination tool gets smarter and figures out which > units need to be "preregistered" and which don't. As I mentioned in my previous response to Randy, it would be just fine if the compilation-order-determination tool were to systematically preregister each and every unit, and then run the normal ordering algorithm, ignoring "limited with" clauses. I would think that compiler writers would prefer to go that way, rather than breaking each and every compilation script in the universe. So I am arguing that the new step should be essentially invisible to programmers. In an implementation where the preregistration step would be costly, then yes, the ordering algorithm might have to be modified to avoid preregistration when possible. ************************************************************** From: Erhard Ploedereder Sent: Tuesday, February 4, 2003 7:38 AM I like "limited with". It is the closest we got to what the user expects. I would like to see a writeup. (I have had bellyaches with Tuck's proposal of tying the cyclic dependency to child packages. I waited for the aches to go away. They did not.) ************************************************************** From: Randy Brukardt Sent: Tuesday, February 4, 2003 3:00 PM > > At the moment, I think it's too hard to implement (given that type stubs are > > expected to take 4 hours of work; this would be more like 4 weeks). But that > > may be irrelevant. More important is that I don't see any way, within the > > current design of Janus/Ada, to implement something like this. > > Not sure how you came up with the 4 hours estimate, but I'm sure it would take > me more than that to do a detailed design. I did a detailed design (in my head) when I was unable to sleep Saturday night. > At this point I'd say that the implementation effort for any of these proposals > would be in the range 4 weeks .. 4 months, and I can't be more specific without > doing a detailed analysis. But anyway, I don't think that numbers are very > interesting at this point. It's really the essence of the implementation > difficulties that need to be looked at. The reason I was thinking about it was that the restricted type stubs model pretty much matches how Janus/Ada works internally anyway. So the main cost is "connecting" the stub to the completion. (And many stubs works, because it's a one-way pointer. But I wouldn't expect that to be true on other implementations.) Since incomplete and private types use the same code in most circumstances, the visibility implications of "availability" should be free. (That is the biggest assumption, of course, because there are no such rules in Ada 95.) So all of the work is doing the connection, and a brute force version (walk all of the symbol table looking for stubs, then when a stub is found, walk the whole symbol table looking for a connection) would only need to be called in one place. Most of the work is figuring out where to call that routine. A better version would save the stubs in a list as they are loaded, so we wouldn't have to search for them. That version would have no distributed cost and not a lot of cost even when stubs are used, so I doubt its possible to do better. Tucker's C.T adds a lot of messing around with ghost packages, but otherwise is identical (we don't have to look quite so far for a completion, but the process is the same). Which is why I'm certain its more work. The current stub stubs requires two stubs of the same completion to match, even when the completion isn't available. That's more work, but I don't think it's a very significant amount (it's just another weird special case in the type matcher, just like anonymous access types and T'Class). In all of these cases, I wouldn't be surprised to find glitches, but that would require a pretty complete test suite. Building that would take longer than doing the implementation (that is often true). Limited with would require a new kind of symbol file, or something (Pascal suggests a hash). It also would require going in an adding a bunch of stuff to the make tool. No one understands how that thing really works, so that alone is a daunting project. And the failure to provide any sort of solution to the access type problem makes it feel like "with type" all over again (which isn't surprising, because it *IS* essentially "with type", just as "type C.T" is just a different syntax for type stubs). ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 3:17 PM > Since incomplete and private types use the same code in > most circumstances, the visibility implications of "availability" should be > free. Aren't you concerned about types having three views (incomplete, partial, full) in the case of an incomplete type completed by a private type? That gives me the willies, to borrow Tuck's words, because it's currently very complicated in our compiler to decide what properties of a type are visible at a given place (because of the separation between partial and full view, because of characteristics that become visible "later within the immediate scope", etc.). Adding a third view is not going to make this easier. ************************************************************** From: Randy Brukardt Sent: Tuesday, February 4, 2003 3:43 PM No, because we have separate type records for each view, and they have their own visibility. For type matching purposes, the only question is whether you can or cannot walk the pointer to the 'next' view, as we always match (and use for other purposes) the "fullest" view available. That depends on visibility, etc. (We have a separate set of routines that always goes to the completion, which the intermediate code generation uses. To heck with visibility at that point.) Since this stuff is recursive (well, actually it uses a loop), you'd first try to go from the incomplete block to the private block (depending on the visibility of the private), and then, once you had the private, you'd do the checks that we currently have. So I don't see a problem with three parts or ten parts for that matter. But I realize that if you somehow managed to implement types with a single record for both views (we tried that and concluded it was impossible. But perhaps we weren't clever enough. Or perhaps its impossible because of shared generics.), it might be much worse. But I think that any proposal has to allow stubs (or whatever) of private types. So you're going to have the three view issues, and if that is a problem, any solution to this problem will be a nightmare to implement. In which case you ought to stay out of the implementation difficulty debate altogether. :-) :-) ************************************************************** From: Gary Dismukes Sent: Tuesday, February 4, 2003 3:20 PM Pascal wrote: > In my proposal it would also be possible for unit P to say "limited with P", but > I think we would want to forbid that, as it seems methodologically dubious. (If > you follow the model to its logical conclusions, it would for instance allow > forward references to packages and types within the spec of P.) One question is, what does it mean when you say "limited with P.C.D". Is it equivalent to having limited with for P, P.C, and P.C.D, in analogy with normal with clauses? That was my first thought, and it seems natural to make it behave the same, but perhaps it's more reasonable to say it only applies to the final named unit. That would avoid the issue of self-circular references when a parent withs its child and we would also want the rules to disallow a direct limited with of yourself. Another small issue is whether use clauses are allowed for packages named in these with clauses. I imagine that the implementation is effectively going to create a package entity containing an incomplete type representative for each type in the real package's visible part (along with representatives for any nested packages presumably) and since this will look essentially the same as other packages in the symbol table environment I don't see any technical problem with permitting a use clause for these packages. Perhaps there are methodological reasons for not allowing it, though I can't think of any. Changing a limited with to a normal with could introduce new illegalities due to name clashes, but that's no worse than adding a new with and use clause for some package. ************************************************************** From: Pascal Leroy Sent: Tuesday, February 4, 2003 3:30 PM > One question is, what does it mean when you say "limited with P.C.D". > Is it equivalent to having limited with for P, P.C, and P.C.D, in > analogy with normal with clauses? That was my first thought, and > it seems natural to make it behave the same, but perhaps it's more > reasonable to say it only applies to the final named unit. That > would avoid the issue of self-circular references when a parent > withs its child and we would also want the rules to disallow > a direct limited with of yourself. From a pedagogical standpoint it would seem simpler to say that "limited with" works like "with", i.e. that "limited with P.C.D" is equivalent to "with P, P.C, P.C.D". Any other option is going to unnecessary confuse users. So maybe the right answer is that if a package has a "limited with" of itself it has no effect (but is not illegal). > Another small issue is whether use clauses are allowed for packages > named in these with clauses. I imagine that the implementation is > effectively going to create a package entity containing an incomplete > type representative for each type in the real package's visible part > (along with representatives for any nested packages presumably) > and since this will look essentially the same as other packages > in the symbol table environment I don't see any technical problem > with permitting a use clause for these packages. Yes, I was thinking that use clauses and package renamings would be OK in that context, although if they lead to problems we could disallow them. But I can't think of any problem at this point. ************************************************************** From: Robert Dewar Sent: Tuesday, February 4, 2003 10:19 PM > In C, if you have a type t you automatically get a pointer type t*. In Ada, > I often wish we had a similar capability, where I wouldn't have to declare > all these silly access types and convert between them explicitly. For some reason, I never really understood it, this proposal, which I was strongly in favor of, never got significant support (I had suggested early on calling it type'access). ************************************************************** From: Tucker Taft Sent: Tuesday, February 4, 2003 3:27 PM In discussing this further with Bob Duff, and mulling it over more in my mind, I see the following restrictions would probably be needed on the "limited with" capability. I am using the term "limited view" of a package to be what you get when you mention a package in a limited-with-clause. I am presuming that a limited view of a package contains only nested packages and types, and all the types are [tagged] incomple. A "limited with" *cannot* make the following declarations visible: 1) Package instantiation Because the generic being instantiated needs to be visible if we are to determine what types it contains, and identifying the generic may require resolving a name that is only use-visible, or that is from some other compilation unit 2) Package renaming Because the package being renamed needs to be visible if we are to determine what types it contains, and identifying the renamed package may require resolving a name that is only use-visible, or that is from some other compilation unit A "limited view" of a package *cannot* be mentioned in: 3) A "use" clause Because we can get a funky kind of Beaujolais effect if we have a "use" clause for two different packages, and due to changing a "normal" with clauses inherited from some ancestor unit the meaning of an identifier switches from one thing to another; e.g.: package A is X : Integer := 7; end A; package B is X : Integer := 203; end B; with A; -- change this to "with B;" and see what happens package P is end; limited with A, B; use A; use B; -- Probably shouldn't be allowed if only have limited view package P.C is Y : Integer := X; -- which X is this? end P.C; This is presuming that if we were to allow a "use" clause for a limited view of a package, it would make only the types and subpackages directly visible. If the "use" clause made everything in the package visible part visible, but only the types were actually "usable" that could work, but having visible but unusable declarations of *all* kinds, rather than just types and packages, could significantly increase the effort of building up a limited view. 4) A package renaming declaration This restriction is probably not as critical as the others. The problem comes when someone from outside the unit containing the limited with references this package renaming. What does it see? If it has a "regular" with for the target of the limited-with, does it see the "full" view of the package via the renaming, or only the limited view of it. Possible restriction (4) brings up the issue of the opposite situation, where a given unit has a limited view on a package, but also has visibility on a renaming of a full view of the package. There seem to be various possibilities: a) The renaming also provides only a limited view when referenced from the given unit. b) The given unit has a full view of the package, through either the renaming or the name introduced by the limited-with clause c) The given unit has a limited view via the name introduced by the limited-with clause, and a full view via the renaming, and they are essentially completely unrelated packages. d) (c), except the packages are recognized as different views of the same package, and the incomplete types in the limited view are recognized as being completed in the renaming, and so are treated as non-limited types for all intents and purposes. (a) is probably the most consistent with the way package renamings work now, in that what children you see via a renaming of a library unit package is determined by the "with" clauses in the unit referencing the renaming, rather than by the with-clauses in the unit containing the renaming. (b) and (d) are both similar to the choice I suggested for "type C.T;" where if the completing type declaration is visible (including presumably via a renaming), then the incomplete type declaration is hidden from all visibility. In the "type C.T;" proposal, this presumably implies that it is as though the "type C.T;" declaration were not there at all, so if a renaming "package R renames C;" is visible, but "C" itself is not visible (because C was not directly "withed"), then you *cannot* refer to the type at all via "C.T." You can refer to it via "R.T" and then of course it is a full type. There are other possible ways of handling this for "type C.T;" but this approach required the smallest change to 18.3(19) and at least makes some kind of sense. For all proposals, the issue of visible renamings of the enclosing package when the package itself is not visible is thorny. A related question is when the completing type is *not* visible (including not via a renaming), but a declaration of a subtype *is* visible, or an object of the type is visible. The questions are always: i) Is the type named via the "limited view" name complete or incomplete? ii) If incomplete, does it nonetheless "match" the complete type in certain contexts? To put it more concretely: package P1 is type A is access ; -- incomplete view is visible via a limited with, type stub, type C.T, or whatever Y, Z : A; end; package P2 is type T is ; end P2; with P2; package P3 is X : ; end P3; with P1, P3; -- No "with" of P2, no visibility on P2.T procedure P4 is begin ... P3.X := P1.Y.all; -- when is this legal? (*) P1.Y.all := P3.X; -- same question (**) if P1.Y.all = P1.Z.all then -- same question (***) ... Hopefully the answers to (*) and (**) are the same, though the asymmetric Name Resolution wording of 5.2(4) on assignment statements seems like (*) and (**) might be treated differently unless we are careful. Note that last time we worked on the "availability" rules for type stubs, we required that either we be in the scope of a with clause for the enclosing package (or a renaming thereof, I presume), or we be in a context where we have the complete type "nearby" (I forget the wording at this point). This would say that having a visible (but not "with"ed) renaming of the enclosing package, or having a visible subtype, wouldn't help. For the above cases, this would mean that probably (*) and (**) would be legal, but (***) would not be legal. Have a nice day... ;-) ************************************************************** From: Tucker Taft Sent: Tuesday, February 4, 2003 3:48 PM Pascal Leroy wrote: > Randy wrote: > > One presumes that you'd have a new kind of symbol table file that held a > > 'lightly' compiled package spec for limited withs. And you'd use the time > > stamp of that file to create the serial number for obsoleteness checking. > > Clearly, when you 'really' compiled the spec, the new kind of package spec > > would have to be updated (otherwise, it wouldn't necessarily match the > > source file, because the source file could have been edited). > > And we would indeed have the same problem. We would first created a "parsed" > Diana tree (during "superficial" compilation) and then we would rewrite it to > make it "analyzed" (during "full" compilation). Surely that would change the > timestamp... After talking with Bob Duff about this a bit, I don't think this really works. I think you need to keep around indefinitely both a "full view" of the package and a "limited view" of the package. Even after you do a "full" compile, some units can still do "limited with" and should only see the limited view. What I would recommend is you treat them as pretty much distinct units. When you fully compile, if there is a limited view already present, you could check that it is compatible with the full view (i.e. has the same names in the package/type tree, and the same types are tagged). I think if they are not compatible, then and only then does it seem worth obsoleting the limited view and any units compiled against it. There seems no point in hashing the incomplete view, since it is easy enough to check compatibility. Hashing would only be an optimization if this compatibility check turned out to be really expensive. If a unit requests a limited view, and the only non-obsolete thing you have around is the full view, I would at that point create the limited view from the full view. I would bump the time stamp on the limited view only when it is created or updated, and units compiled against the limited view only worry about that time stamp. The time stamp of the full view is irrelevant to them. ************************************************************** From: Randy Brukardt Sent: Tuesday, February 4, 2003 4:13 PM "Easy enough to check compatibility"? You've got to be kidding. The only thing that we know how to do with a symbol file is load them into a symbol table. But you can't do that to "check compatibility", because you've got to load the full view into the same place (can't have two packages with the same library-level name). Pascal's hash idea (instead of a time stamp) makes much more sense. You'd create the limited symbol file for every compilation (full and limited) that you did, but the hash would only change if it actually is different. Keep in mind that no one actually "obsoletes" anything. How we do obsoleteness checking is simply to compare the time stamp serial numbers of every unit withed transitively for every unit withed. They better all match. Any mismatch is reported as an error, and the compilation aborted. And we of course repeat the check when linking. We use the order only to provide better error messages, and units are NEVER removed from the program library (unless the programmer does so manually). ************************************************************** From: Tucker Taft Sent: Tuesday, February 4, 2003 5:05 PM > "Easy enough to check compatibility"? You've got to be kidding. The only > thing that we know how to do with a symbol file is load them into a symbol > table. But you can't do that to "check compatibility", because you've got to > load the full view into the same place (can't have two packages with the > same library-level name). This is my whole point. You *do* need to have both views available at once, since one unit might have a "limited with" of package P, while some unit that it depends on indirectly has a full "with" of P. To avoid ripple effects, we want the one with the limited-with to only see the limited view. You mentioned in another note that you have separate symbol table entries for various different forms of a type. I think you will need separate symbol table entries for the limited view and the full view. In other words, for a library package P, you really have two units, one whose library-level name is "P-full" and one whose name is "P-limited". > Keep in mind that no one actually "obsoletes" anything. How we do > obsoleteness checking is simply to compare the time stamp serial numbers of > every unit withed transitively for every unit withed. They better all match. Yes, I understand that approach. I probably should have simply talked in terms of bumping time stamps. I think the same point can be made in those terms. We have a similar model, albeit only in the program library we build up in memory. [Since we allow the compiler to run for a "long" time and to process different versions of the same file during a single execution, we have all the obsoleteness checking mechanism in there as well.] ************************************************************** From: Randy Brukardt Sent: Tuesday, February 4, 2003 5:34 PM > This is my whole point. You *do* need to have both views available > at once, since one unit might have a "limited with" of package P, > while some unit that it depends on indirectly has a full "with" of P. > To avoid ripple effects, we want the one with the limited-with > to only see the limited view. I don't see that. Each compilation has its own symbol table, and I don't see any reason why the mere fact of withing something that saw a limited view of some package was anything to do with a package that sees the full view. > You mentioned in another note that you have separate symbol table > entries for various different forms of a type. No, "types" aren't in the symbol table at all. They have their own separate table. Type names are in the symbol table, of course, as are component names. But a type name has nothing to do with a type -- they're completely separate concepts (as they are in Ada). > I think you will > need separate symbol table entries for the limited view and > the full view. In other words, for a library package P, you really have > two units, one whose library-level name is "P-full" and one whose name > is "P-limited". If that's true, we're getting into horrific complexity territory. A fundamental basis of the symbol table is that most entities have only one name and cannot be overloaded. You're asking that all of the lookup code be changed to be able to handle packages with two views. Along with all of the declaration code (so it can write into the correct view). Moreover, that would be true in every compiler (source based or library based). If the proposal requires both copies in the symboltable, then I think the proposal should be killed as soon as possible. If not sooner. ************************************************************** From: Pascal Leroy Sent: Wednesday, February 5, 2003 3:40 AM > I think you need to keep around indefinitely both a "full view" of > the package and a "limited view" of the package. Even after you > do a "full" compile, some units can still do "limited with" and should only > see the limited view. What I would recommend is you treat them as > pretty much distinct units. In terms of language description, you're right, a unit that does a "limited with" must see the limited view even for a unit that has been fully compiled. However, the implementation you suggest might make sense for your compiler, it doesn't make sense for ours (or for Randy's if I understand him right). We fundamentally depend on the invariant: 1 Ada unit = 1 Diana tree. Changing this is not an option. This means that if we see "limited with P" and P has been fully compiled, then we need to simulate/synthesize a limited view for P. For name resolution, this is simple enough. When we see the name P.T we do a lookup of the string "T" in the identifier table for P. The lookup has to succeed if and only if T is part of the limited view. We would probably do that by examining the Diana tree for T. Another option would be to store a bit "yes, I am part of the limited view" on the defining identifier for T. Once name resolution has been done, we will have access to the full tree for T. At this point we will need to behave as if T was incomplete. I can think of at least 3 ways to achieve this in our compiler, but the simplest one is probably to use a predicate "is this an incomplete type?" where appropriate, and have that predicate determine whether visibility was obtained though a "limited with". We already have such a predicate, but I'm pretty sure that we don't call it everywhere it would be needed. This certainly looks like work, but we are not talking man-years here. And there is certainly no need to keep two trees for each unit. ************************************************************** From: Jean-Pierre Rosen Sent: Wednesday, February 5, 2003 3:32 AM > Add a legality rule: > > A library_item mentioned in a limited_with_clause shall be a > package_declaration[, not a subprogram_declaration, generic_declaration, > or generic_instantiation]. Why not generic_instantiation? 1) not really different from a package declaration 2) quite useful, since instantiations are commonly used for building objects with multiple facets. Later you say: >We do not allow a limited_with_clause to mention a generic >instantiation, because that would require all kinds of semantic analysis >stuff, such as visibility. Well, the instantiation has been compiled at that point, so all visibility should be solved. Maybe worth some further investigation ************************************************************** From: Pascal Leroy Sent: Wednesday, February 5, 2003 3:59 AM The restriction is perfectly sensible. We don't want to have to do any name resolution to build the limited view, and in order to determine what generic we are talking about, we would have to do a pretty extensive name resolution (use clauses, parent units, renamings, etc.). Moreover, in order to build the instantiation, we would have to have compiled the specification of the generic already, but limited views cannot have compilation prerequisites (if they had, we could run into circularity problems). ************************************************************** From: Robert A. Duff Sent: Wednesday, February 5, 2003 8:22 AM Tucker and I discussed this issue yesterday, and we decided that although it *is* troubling to make rules that cause package instances to be different from normal packages, it is not feasible to implement the proposal (in some compilers) without this restriction. If there's a cycle, then the instantiation might *not* have been compiled yet. How about: limited with A; package B is new Some_Generic(...); limited with B; package A is new Some_Generic(...); It cannot be the case that A and B have both been *fully* compiled before each other. I really think a key feature of this proposal is that when the compiler sees "limited with X", it can determine the list of incomplete types in X in a purely syntactic manner. It should not have to do any kind of heavy-duty semantic analysis. It should not have to look at any source text outside of X. It's not *so* bad, because if you wanted to do cycles like the above, you can always break the cycle by adding more generic formal parameters. ************************************************************** From: Jean-Pierre Rosen Sent: Wednesday, February 5, 2003 11:28 AM Yes. I see the problem, and I have sympathy for the poor compiler writers. But with my teacher's hat on, I don't feel very easy to explain that in some cases, an instantiation is not equivalent to a regular package. ************************************************************** From: Tucker Taft Sent: Wednesday, February 5, 2003 5:47 AM Pascal Leroy wrote: >... > > From a pedagogical standpoint it would seem simpler to say that "limited > with" works like "with", i.e. that "limited with P.C.D" is equivalent to > "with P, P.C, P.C.D". Any other option is going to unnecessary confuse > users. > > So maybe the right answer is that if a package has a "limited with" of > itself it has no effect (but is not illegal). Yes, that is preferable. > Yes, I was thinking that use clauses and package renamings would be OK in > that context, although if they lead to problems we could disallow them. But > I can't think of any problem at this point. I sent a note about this, titled "Restrictions on limited-with". Have you seen it? Any comments? I think "use" clauses are a bad idea. Renaming is tricky, and needs careful thought. ************************************************************** From: Pascal Leroy Sent: Wednesday, February 5, 2003 6:23 AM Yes, I saw it, but after sending the above message. I agree that use clauses cause a Beaujolais-ish effect (maybe this should be named the Chianti effect ;-) and should be disallowed. I also agree that renamings should probably be disallowed since they cause more trouble than they are worth. ************************************************************** From: Robert Dewar Sent: Wednesday, February 5, 2003 7:18 AM So far it seems to me that the limited-with discussion looks like it is very promising. I must say I was unhappy with the idea of furiously trying to get a resolution in Padua (and I was trying to figure out how to molest my schedule to attend :-) but now I am actually getting some confidence that a) this is going in the right direction b) there really is a good possibility of agreement Very encouraging :-) We will definitely try to prototype this in GNAT as soon as there is a reasonably well defined proposal. **************************************************************