!standard 03.02 (04) 04-11-12 AI95-00326/08 !standard 03.02 (05) !standard 03.07 (01) !standard 03.10.01 (02) !standard 03.10.01 (04) !standard 03.10.01 (05) !standard 03.10.01 (08) !standard 03.10.01 (09) !standard 03.10.01 (10) !standard 03.10.01 (11) !standard 10.01.01 (12) !standard 13.01 (11/1) !standard J.11 (00) !class amendment 02-02-06 !status Amendment 200Y 04-01-09 !status WG9 Approved 04-06-18 !status ARG Approved 10-0-3 03-12-12 !status work item 03-07-30 !status ARG Approved 10-0-1 03-07-15 !status work item 02-12-13 !status received 02-02-06 !priority Medium !difficulty Medium !subject Incomplete types !summary A concept of a "tagged incomplete" type is introduced, which is a type that has the same restrictions as an incomplete type except that it may also be used for a formal or actual parameter. !problem 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 such types as formal parameters, since from a code generation point of view they are essentially equivalent to access parameters. !proposal The syntax for an incomplete type declaration is amended as follows: incomplete_type_declaration ::= TYPE defining_identifier [discriminant_part] [IS TAGGED]; If the words IS TAGGED appear, the type is a tagged incomplete type. Otherwise, the type is a non-tagged incomplete type. In addition to places where a (non-tagged) incomplete type may be used, a tagged incomplete type may be used as a formal parameter type, and a dereference of an access to tagged incomplete can be used as an actual parameter. The attribute 'Class is defined for (specific) tagged incomplete types. (The use of 'Class with non-tagged incomplete types is considered obsolescent; such non-tagged incomplete types must be completed with a tagged type.) The class-wide type denoted by the Class attribute of an incomplete tagged type is also an incomplete tagged type. An incomplete tagged type declaration must be completed by a tagged type declaration. !wording Note: This wording includes the changes needed to support AI-217-6. These rules are marked. Replace 3.2(4) by: The composite types are the record types, record extensions, array types, task types, and protected types. There can be multiple views of a type with varying sets of operations. An incomplete type represents an incomplete view (see 3.10.1) of a type with a very restricted usage, providing support for recursive data structures. A private type or private extension represents a partial view (see 7.3) of a type, providing support for data abstraction. The full view (see 3.2.1) of a type provides its complete declaration. An incomplete or partial view is considered a composite type. AARM: Only the first and last sentences really define anything; the real definitions of the views are in the referenced paragraphs. Modify 3.2(5): Certain composite types (and [partial] views thereof) have special... Add to 3.2.1(8) after the first sentence: A full type defines the full view of a type. AARM Change: All types have full views in this model. The penultimate sentence of 7.3(4) gets square brackets in the AARM (but I'd recommend leaving it there in order to keep the entire private type model intact). Modify 3.7(1) to remove the two occurrences of "partial" in the last sentence. Note: Incomplete views can have unknown_discriminant_parts, and this wording should reflect that. Since full views cannot have unknown_discriminant_parts, "view of a type" is enough here. Replace 3.10.1(2): incomplete_type_declaration ::= TYPE defining_identifier [discriminant_part] [IS TAGGED]; Modify 3.10.1(4): Add a new first sentence: If an incomplete_type_declaration includes the reserved word tagged, then a full_type_declaration that completes it shall declare a tagged type. Replace 3.10.1(5-9): A name that denotes an incomplete view of a type may be used as follows: * as the subtype_mark in the subtype_indication of an access_to_object_definition; the only form of constraint allowed in this subtype_indication is a discriminant_constraint; * as the subtype_mark in an access_definition. If such a name denotes a tagged incomplete view, it may also be used: * as the subtype_mark defining the subtype of a parameter in a formal_part; * as the prefix of an attribute_reference whose attribute_designator is Class; such an attribute_reference is restricted to the uses allowed here; it denotes a tagged incomplete view. If such a name occurs within the list of declarative_items containing the completion of the incomplete view, it may also be used: * as the subtype_mark defining the subtype of a parameter or result of an access_to_subprogram_definition. If any of the above uses occurs as part of the declaration of a primitive subprogram of the incomplete view, and the declaration occurs immediately within the private part of a package, then the completion of the incomplete view shall also occur immediately within the private part; it may not be deferred to the package body. AARM: This fixes a hole in Ada 95 where a dispatching operation with an access parameter could be declared in a private part and a dispatching call on it could occur in a child even though there is no visibility on the full type, requiring access to the controlling tag without access to the representation of the type. Replace 3.10.1(10): A prefix shall not be of an incomplete view. AARM: We used to disallow all dereferences of an incomplete type. Now we only disallow such dereferences when used as a prefix. Dereferences used in other contexts do not pose a problem since normal type matching will preclude their use except when the full type is "nearby" as context (e.g. as the expected type). Replace 3.10.1(11): An incomplete_type_declaration declares an incomplete view of a type, and its first subtype; the first subtype is unconstrained if a known_discriminant_part appears. If the incomplete_type_declaration includes the reserved word tagged, it declares a tagged incomplete view. An incomplete view of a type is a limited view of the type (see 7.5). AARM Change: We define "incomplete view" and "tagged incomplete view" here. We explicitly say that "incomplete view"s do not have assignment. Given an access type A whose designated type T is an incomplete view, a dereference of a value of type A also has this incomplete view except when: * it occurs in the immediate scope of the completion of T, or * it occurs in the scope of a nonlimited_with_clause that mentions a library package in whose visible part the completion of T is declared. In these cases, the dereference has the full view of T. AARM Note: We need the "in whose visible part" rule so that the second rule doesn't trigger in the body of a package with a with of a child unit: package P is private type T; type PtrT is access T; end P; private package P.C is Ptr : PtrT; end P.C; with P.C; package body P is -- Ptr.all'Size is not legal here, but it is in the scope of a -- nonlimited_with_clause for P. type T is ... -- Ptr.all'Size is legal here. end P; AI-217 Note: The above rules come from AI-217-6. Note that here we say that the view is complete, and thus none of the restrictions outlined in this section apply, not just dereferencing as the AI-217-6 wording said. That is what was intended by AI-217-6 (the wording was faulty). 4.1(9): No change is needed. Note that this differs from the proposed AI-217-6. The second bullet of the new text following 10.1.1(12) from AI-217-6 should read: * For each type_declaration in the visible part, an incomplete view of the type is declared. If the type_declaration is tagged, then the view is a tagged incomplete view. Modify 13.1(11/1): Replace the second sentence by: Operational and representational aspects are the same for all views of a type. Note: This change isn't strictly necessary; you can't actually tell those aspects for an incomplete view. But it is an important part of the "view of a type" model. We could add a clarification like "Thus, the aspects of an incomplete or partial view are the same as those of the full view of the type." Add J.11: J.11 The Class Attribute of Untagged Incomplete Types For the first subtype S of a type T declared by an incomplete_type_declaration that is not tagged, the following attribute is defined: S'Class Denotes the first subtype of the incomplete class-wide type rooted at T. The completion of T shall declare a tagged type. Such an attribute reference shall occur in the same library unit as the incomplete_type_declaration. !discussion Tagged type hierarchies often have need for mutually dependent types. By allowing a tagged incomplete type to be used as a formal parameter type, access parameters may be avoided in such mutually dependent type situations. This proposal gains additional power when combined with the proposals for allowing mutually dependent types to be defined in separate packages. We are not allowing an incomplete tagged type to be used as a return type, because of the many special rules and implementation issues associated with returning tagged types (e.g. abstract types may not be used as result types, functions that must be overridden, accessibility checks on returning limited by-reference types, dispatching on result coupled with tag indeterminacy, finalization associated with returning potentially controlled types, etc.). 3.2.1(8) is the only place where an incomplete type is described as a "view". Elsewhere in the standard, it is described as a separate type. However, the separate type model has a number of bad effects (for instance, nowhere in the standard does it say that an incomplete type matches its completing type - if the types are different, that's necessary). Moreover, private types are described in the standard as a partial view of a type. An incomplete type is just another kind of restricted view, so it is very strange that the description is completely different. In addition, incomplete types are omitted from various text in the standard; if they're separate types, there would be quite a bit of patching to do. Thus, the wording generalizes the concept "view of a type" to encompass incomplete views as well as partial views. We considered generalizing the concept of a "partial view of a type" instead. However, that led to lengthy terms for specific kinds of partial views: "tagged incomplete partial view" and "limited private partial view" are just too unwieldy. A possible hole was discovered during this discussion: package P is ... private type T1; type AT1 is access all T1'Class; P1 : AT1; procedure Foo (P : access T1); -- OK in Ada 95. type T2 is tagged; type AT2 is access all T2'Class; P2 : AT2; procedure Bar (P : T2); end P; private package P.C is procedure Test; end P.C; package body P.C is procedure Test is begin Foo (P.P1); -- A dispatching call! (Ada 95) Bar (P.P2.all); -- A dispatching call! (Ada 0Y) end Test; end P.C; If the body of P sets P1 and P2 to objects, the calls to Foo and Bar are dispatching calls and logically ought to work. But the location of the tags for T1 and T2 are unknown when the calls are compiled. This hole is fixed by adding a rule which prevents the completion of an incomplete type from being deferred to the body if the type has any primitive operations. Thus types T1 and T2 are missing completions in the example above, and the example is therefore illegal. Dereferences of incomplete tagged types are still useful for class-wide calls. For instance (presuming AI-217-6 is approved): limited with Departments; package Employees is type Employee is private; procedure Assign (E : Employee; D : Departments.Department'Class); type Dept_Ptr is access Departments.Department'Class; private ... end Employees; with Employees; procedure Recruit (D : Employees.Dept_Ptr; E : Employees.Employee) is begin Employees.Assign (E, D.all); end Recruit; From the perspective of Recruit, a Departments.Department is just a black box that Employees uses and it was passed and is passing along. It would be bizarre for Recruit to have to with Departments simply to be able to call Assign. Ada 95 introduced the rule forbidding all dereferences. However, this turns out to be stronger than necessary. The problem cases are those where: * A component of the dereferenced item is read (a discriminant or tag); * A property of the dereferenced item is used (like 'Size); * The dereferenced item is of an untagged type and is passed in a subprogram call (in this case, the parameter passing mechanism -- by copy or by reference -- is not known); and the complete type is not available. The last case is interesting. In order for this to happen, we have to have a parameter of an untagged incomplete type which is either deferred to the body, or is an incomplete view generated from the limited view of a package. Ada 95 allows this only for access-to-subprogram types. However, being able to dereference access-to-incomplete objects is a useful capability, especially when an object of the complete type is available via another withing path. It seems better to simply disallow the access-to-subprogram case when it happens, especially as it seems to be more of an unintended consequence of the rules than an intended use of incomplete types. Thus, we adopt a rule which does not allow incomplete views to be used in access-to-subprogram type definitions unless the completion of the incomplete type is in the same declaration list. (For such a type, the completion would always be available anywhere that an object could be created.) That means that such types are not allowed if the completion is deferred to the body, or for incomplete views from a limited view of a package. With this rule in hand, we only need to disallow dereferences which are prefixes (the first two cases above). Note that this AI corrects the interpretation of AI-183. In particular, the second example from AI-183: package P2 is private type T1; type T2; procedure P (X1 : access T1; X2 : access T2); -- Illegal. end; Is now illegal because the types are used in the declaration of a primitive operation, but are not completed in the private part. The legality no longer depends on the full type declarations. !example Here is the example from AI-217-6, recast to use the additional capabilities of tagged incomplete types. These allow the elimination of the explicit access types from the parameters of the subprograms. Note that the function return still requires an explicit access type, because neither tagged incomplete views nor anonymous access types are allowed there. limited with Departments; package Employees is type Employee is tagged private; procedure Assign_Employee(E : in out Employee; D : in out Departments.Department'Class); type Dept_Ptr is access all Departments.Department'Class; function Current_Department(D : in Employee) return Dept_Ptr; ... end Employees; limited with Employees; package Departments is type Department is tagged private; procedure Choose_Manager(D : in out Department; Manager : in out Employees.Employee'Class); ... end Departments; !corrigendum 3.2(4) @drepl The composite types are the @i types, @i, @i types, @i types, and @i types. A @i type or @i represents a partial view (see 7.3) of a type, providing support for data abstraction. A partial view is a composite type. @dby The composite types are the @i types, @i, @i types, @i types, and @i types. There can be multiple views of a type with varying sets of operations. An @i type represents an incomplete view (see 3.10.1) of a type with a very restricted usage, providing support for recursive data structures. A @i type or @i represents a partial view (see 7.3) of a type, providing support for data abstraction. The full view (see 3.2.1) of a type provides its complete declaration. An incomplete or partial view is considered a composite type. !corrigendum 3.2(5) @drepl Certain composite types (and partial views thereof) have special components called @i whose values affect the presence, constraints, or initialization of other components. Discriminants can be thought of as parameters of the type. @dby Certain composite types (and views thereof) have special components called @i whose values affect the presence, constraints, or initialization of other components. Discriminants can be thought of as parameters of the type. !corrigendum 3.2.1(8) @drepl A named type that is declared by a @fa, or an anonymous type that is defined as part of declaring an object of the type, is called a @i. The @fa, @fa, @fa, or @fa that defines a full type is called a @i. Types declared by other forms of @fa are not separate types; they are partial or incomplete views of some full type. @dby A named type that is declared by a @fa, or an anonymous type that is defined as part of declaring an object of the type, is called a @i. A full type defines the @i of a type. The @fa, @fa, @fa, or @fa that defines a full type is called a @i. Types declared by other forms of @fa are not separate types; they are partial or incomplete views of some full type. !corrigendum 3.7(1) @drepl A composite type (other than an array type) can have discriminants, which parameterize the type. A @fa specifies the discriminants of a composite type. A discriminant of an object is a component of the object, and is either of a discrete type or an access type. An @fa in the declaration of a partial view of a type specifies that the discriminants of the type are unknown for the given view; all subtypes of such a partial view are indefinite subtypes. @dby A composite type (other than an array type) can have discriminants, which parameterize the type. A @fa specifies the discriminants of a composite type. A discriminant of an object is a component of the object, and is either of a discrete type or an access type. An @fa in the declaration of a view of a type specifies that the discriminants of the type are unknown for the given view; all subtypes of such a view are indefinite subtypes. !corrigendum 3.10.1(2) @drepl @xcode<@fa@ft<@b>@fa< defining_identifier [discriminant_part];>> @dby @xcode<@fa@ft<@b>@fa< defining_identifier [discriminant_part] [>@ft<@b>@fa<];>> !corrigendum 3.10.1(4) @drepl If an @fa has a @fa, then a @fa that completes it shall have a fully conforming (explicit) @fa (see 6.3.1). If an @fa has no @fa (or an @fa), then a corresponding @fa is nevertheless allowed to have discriminants, either explicitly, or inherited via derivation. @dby If an @fa includes the reserved word @b, then a @fa that completes it shall declare a tagged type. If an @fa has a @fa, then a @fa that completes it shall have a fully conforming (explicit) @fa (see 6.3.1). If an @fa has no @fa (or an @fa), then a corresponding @fa is nevertheless allowed to have discriminants, either explicitly, or inherited via derivation. !corrigendum 3.10.1(5) @drepl The only allowed uses of a @fa that denotes an @fa are as follows: @dby A @fa that denotes an incomplete view of a type may be used as follows: !corrigendum 3.10.1(7) @ddel @xbullet defining the subtype of a parameter or result of an @fa;> !corrigendum 3.10.1(8) @drepl @xbullet in an @fa;> @dby @xbullet in an @fa.> If such a @fa denotes a tagged incomplete view, it may also be used: @xbullet defining the subtype of a parameter in a @fa;> !corrigendum 3.10.1(9) @drepl @xbullet of an @fa whose @fa is Class; such an @fa is similarly restricted to the uses allowed here; when used in this way, the corresponding @fa shall declare a tagged type, and the @fa shall occur in the same library unit as the @fa.> @dby @xbullet whose @fa is Class; such an @fa is restricted to the uses allowed here; it denotes a tagged incomplete view.> If such a @fa occurs within the list of declarative_items containing the completion of the incomplete view, it may also be used: @xbullet defining the subtype of a parameter or result of an @fa.> If any of the above uses occurs as part of the declaration of a primitive subprogram of the incomplete view, and the declaration occurs immediately within the private part of a package, then the completion of the incomplete view shall also occur immediately within the private part; it may not be deferred to the package body. !corrigendum 3.10.1(10) @drepl A dereference (whether implicit or explicit -- see 4.1) shall not be of an incomplete type. @dby A @fa shall not be of an incomplete view. !corrigendum 3.10.1(11) @drepl An @fa declares an incomplete type and its first subtype; the first subtype is unconstrained if a @fa appears. @dby An @fa declares an @i of a type, and its first subtype; the first subtype is unconstrained if a @fa appears. If the @fa includes the reserved word @b, it declares a @i. An incomplete view of a type is a limited view of the type (see 7.5). Given an access type @i whose designated type @i is an incomplete view, a dereference of a value of type @i also has this incomplete view except when: @xbullet, or> @xbullet that mentions a library package in whose visible part the completion of @i is declared.> In these cases, the dereference has the full view of @i. !corrigendum 10.1.1(12) @dinsa A @fa or a @fa is @i if the declaration is immediately preceded by the reserved word @b; it is otherwise @i. A library unit is private or public according to its declaration. The @i of a library unit are the library unit itself, and the public descendants of its public children. Its other descendants are @i. @dinss For each library @fa in the environment, there is an implicit declaration of a @i of that library package. The limited view of a package contains: @xbullet, a declaration of the limited view of that package, with the same @fa.> @xbullet in the visible part, an incomplete view of the type is declared. If the @fa is tagged, then the view is a tagged incomplete view.> !corrigendum 13.1(11/1) @drepl Operational and representation aspects of a generic formal parameter are the same as those of the actual. Operational and representation aspects of a partial view are the same as those of the full view. A type-related representation item is not allowed for a descendant of a generic formal untagged type. @dby Operational and representation aspects of a generic formal parameter are the same as those of the actual. Operational and representation aspects are the same for all views of a type. A type-related representation item is not allowed for a descendant of a generic formal untagged type. !corrigendum J.11(1) @dinsc For the first subtype S of a type T declared by an @fa that is not tagged, the following attribute is defined: @xhang<@xtermDenotes the first subtype of the incomplete class-wide type rooted at T. The completion of T shall declare a tagged type. Such an attribute reference shall occur in the same library unit as the @fa. !ACATS Test ACATS B and C tests should be constructed for this AI. !appendix This AI was split out of AI-00217-05 (and other variants), in order to simplify those proposals and to insure that this idea gets considered no matter which of those proposals is adopted. ************************************************************* From: Randy Brukardt Sent: Tuesday, July 15, 2003 2:25 PM In Toulouse, we discussed whether we could allow dereferences of tagged incomplete types. In general, the answer is no, because we cannot know where tags and discriminants are going to be stored in the completing type. (If the type is completed in the body, for instance.) The conversation in Toulouse focused on dispatching calls. Clearly, we cannot allow dispatching calls because we don't know where the tag is stored (presuming the implementation allows positioning it, as Rational Apex and Janus/Ada do). However, there is no problem with class-wide calls, and these could be useful. The key is that we can only allow "whole-object" dereferences when the dereference is immediately passed as a reference parameter. This would look something like: package Typ is type T is tagged; type AT is access all T; procedure Op1 (Obj : in out T'Class); procedure Op2 (Obj : in out T); ... end Typ; with Typ; procedure User is ... P : Typ.AT := ...; ... Typ.Op1 (P.all); -- Allowed? (No implementation problem). Typ.Op2 (P.all); -- Can't allow (don't know where the tag is). ... end User; Note that Op1 could be used as a work-around to implement Op2 if that was necessary (as the body of Typ.Op1 certainly could dispatch to Op2). This rule would be on the messy side (it pretty much could only allow dereferences when used as a non-dispatching actual parameter) and I haven't tried to figure out the wording. Should we pursue this, or leave that AI without any means of making a call? ************************************************************* From: Randy Brukardt Sent: Tuesday, July 15, 2003 2:41 PM I neglected to mention that class-wide parameters would be the most likely way that a tagged incomplete type would be used from a limited with (you almost always should avoid non-dispatching specific tagged parameters, because they cause trouble if there is an extension). So, such a class-wide call may not be rare: package Office is type Department is tagged private; type Dept_Ptr is access all Department'Class; private ... end Office; limited with Office; package Employees is type Employee is tagged private; type Emp_Ptr is access all Employee'Class; procedure Assign_Employee(E : in out Employee; D : in out Office.Department'Class); ... function Current_Department(D : in Employee) return Office.Dept_Ptr; private ... end Employees; with Employees, Repository; procedure RRS is Randy, Ian, Isaac : Employees; begin -- Add a new employee in Randy's department: Repository.Read (Randy); Employees.Assign_Employee (Isaac, Employees.Current_Department (Randy).all); Repository.Write (Isaac); end RRS; The call to Assign_Employee is illegal for a tagged incomplete type. We'd have to with Office to make it legal, which is annoying, since this has little to do with Office. (And, if we adopt a special rule to make it legal here, which we've discussed, then it ought to be legal for tagged incomplete as well -- otherwise, we'd have a weird inconsistency.) ************************************************************* From: Tucker Taft Sent: Tuesday, July 15, 2003 2:46 PM I would suggest we keep it as simple as possible. If at some later point we think we can open it up more, that is possible, but a really complex rule probably doesn't help anyone at this point. I like your suggestion of allowing class-wide incomplete types only, and disallow any calls with parameters of a non-class-wide incomplete type. That seems pretty simple to define and enforce. I suppose we need to worry about S'Class, where subtype S is T(discrim => value). It seems the prefix of the Class attribute must be the first subtype. ************************************************************* From: Robert I. Eachus Sent: Tuesday, July 15, 2003 8:28 PM Tucker Taft wrote: ... >I like your suggestion of allowing class-wide incomplete types only, >and disallow any calls with parameters of a non-class-wide >incomplete type. Sounds reasonable. > That seems pretty simple to define and >enforce. I suppose we need to worry about S'Class, where >subtype S is T(discrim => value). It seems the prefix of the >Class attribute must be the first subtype. This sent me scurrying off to the AARM, which was perfectly unclear: 3.9 (15) S'Class is unconstrained. However, if S is constrained, then the values of S'Class are only those that when converted to the type T belong to S. 3.9(15b) Note that if S is constrained, S'Class is only partially constrained, since there might be additional discriminants added in descendants of T which are not constrained. In any case, I don't see that your proposed restriction buys anything. I think you are trying to avoid cases where there is a subtype that is more constrained than the base class. But in theory I could restructure things so that S was a first subtype with the same discriminant constraints. So as long as S'Class is allowed where S is a derived type, I don't see the limitation accomplishing anything other than confusing Ada programmers. In any case, since the (class-wide) name must appear in a context where it is going to be resolved by run-time dispatching, any freezing issues are long gone. ************************************************************* From: Tucker Taft Sent: Tuesday, July 15, 2003 2:46 PM It is important we don't allow "partially" constrained subtypes of an incomplete type as actuals, since the caller doesn't know where the discriminants are. However, perhaps we are safe, since named subtypes of incomplete types aren't allowed, so it would not be possible to declare a named subtype S, and hence there would be no way to write a partially constrained (incomplete class-wide) subtype S'Class. ************************************************************* From: Robert I. Eachus Sent: Wednesday, July 16, 2003 11:38 AM Tucker Taft wrote: > It is important we don't allow "partially" constrained > subtypes of an incomplete type as actuals, since the > caller doesn't know where the discriminants are. > However, perhaps we are safe, since named subtypes > of incomplete types aren't allowed, so it would not > be possible to declare a named subtype S, and hence > there would be no way to write a partially constrained > (incomplete class-wide) subtype S'Class. > But the caller may also be passing an object of a type (not subtype) that is totally unknown where the call is made. That is the nature of calls with class-wide formals. Let me give an example of the full horror, and you will see that compilers SHOULD be handling it correctly now: package P is type T(Some: Discriminants) is tagged....; subtype S is T(This_Discriminant); function Some_Function(SP: S) return Integer; end P; with P; use P; package U is procedure Some_Proc(TC: in T'Class); end U; package body U is procedure Some_Proc(TC: in T'Class) is I: Integer := Some_Function(TC); -- discriminant check is here... begin null; end; end U; package Q is type R is new T with private; private ... end Q; with P, Q, U; use P, Q, U; procedure Main is QR: R; begin Some_Proc(QR); end Main; I think that gets it. The discriminant check on the call to Some_Function has to be made by code that doesn't even know that type R will exist. I don't know how current compilers handle this, I have been assuming that if they let users control the layout of discriminants in derived types that the dispatch table includes a thunk for finding discriminants. ************************************************************* From: Robert I. Eachus Sent: Wednesday, July 16, 2003 12:02 PM I think I just misspoke in my example, but it doesn't change the issue. The discriminant check occurs after a view conversion to the subtype of the formal parameter, but the function I used in my example is illlegal where it is declared. (The legality rule in 3.9.2(10/1) prevents it.) So change the example to: package P is type T(Some: Discriminants) is tagged....; subtype S is T(This_Discriminant); function Some_Function(SP: S'Class) return Integer; -- or worse function Some_Other_Function(SP: S'Class) return T; -- now it is dispatching, but the parameter with the subtype check is classwide. end P; with P; use P; package U is procedure Some_Proc(TC: in T'Class); end U; package body U is procedure Some_Proc(TC: in T'Class) is I: Integer := Some_Function(TC); -- discriminant check is here... begin null; end; end U; package Q is type R is new T with private; private ... end Q; with P, Q, U; use P, Q, U; procedure Main is QR: R; begin Some_Proc(QR); end Main; ************************************************************* From: Randy Brukardt Sent: Wednesday, July 23, 2003 6:53 PM Robert Eachus wrote: .. > I think that gets it. The discriminant check on the call to > Some_Function has to be made by code that doesn't even know that type R > will exist. I don't know how current compilers handle this, I have been > assuming that if they let users control the layout of discriminants in > derived types that the dispatch table includes a thunk for finding > discriminants. 13.5.1(14) says that the layout of a parent part (obviously including the discriminants) can't be changed by a record rep. clause. Note that this is a RULE, not a "recommended level of support" -- compilers are not allowed to change the layout of the parent part. So there is no problem here. ************************************************************* From: Robert I. Eachus Sent: Thursday, July 24, 2003 5:44 PM The intent in my example was to have a call on the parent type have to perform a discriminant check necessary because of a subtype of the child type. But I see that Randy is right. A derived type can add a variant part, but it has to depend on an existing discriminant. So as far as the compiler is concerned there is no difference between a subtype of a derived (tagged) type and a subtype of the parent. ************************************************************* From: Randy Brukardt Sent: Wednesday, September 17, 2003 2:05 PM I'm working on the wording for tagged incomplete types, and I need a bit of help. The Standard makes it clear that an incomplete type is a separate type from the completion. (This is very different than the situation for private types. This formulation was inherited from Ada 83.) But that doesn't seem to be reflected in the type matching rules. For instance: (I've marked the incomplete type with an [I] and the complete type with a [C] for clarity) type D[I]; type Acc_D is access all D[I]; type D[C] is ...; P : Acc_D := new D[C]; Where is the rule that says that D[C] is allowed to match D[I] for resolution purposes? Now, we all *know* this works. The question is merely where does the RM say that. If it doesn't, of course, we have another hole to fix, which should be taken into account when writing the wording. ************************************************************* From: Randy Brukardt Sent: Thursday, November 20, 2003, 11:20 PM Following is an analysis of the RM wrt to making an incomplete view a type of partial view. Various uses of "partial view" would need to be qualified with "private". Appendix: All references to "incomplete type" in the AARM other than 3.10.1. 3.1(12.i): informal use. 3.2.1(26.c): informal use. 3.9(15.c): informal use. 7.1(17.c): Applies to type decl, no changes needed. 13.14(1.j): informal use. 13.14(3/1): Applies to type decl, no changes needed. 13.14(3.b): informal use. B.1(26.b): Applies to type decl, no changes needed. --- Appendix: All references to "partial view" in the AARM. 3.7(1): use of unknown discriminants. OK unchanged. (3.10.1 definitely allows unknown_discriminants on incomplete types; but it is not clear that this rule does.) 3.7(26.c): Same as above. 13.1(11/1): Same rep. for both views. Harmless to say (even though you can't query those aspects). 8.3(10.b): An AARM note, and appears true. This is discussing primitive operations. It appears that incomplete types have primitive operations by 3.2.3(6), so the note seems to be true. package Foo is ... private type Arg; procedure Goody (P : access Arg); -- Primitive for Arg. end Foo; 7.5(1.c): An AARM note (about limited types completed by non-limited types). Probably fine as is (and probably wrong because of AI-287 anyway). 3.9.3(9): Says that the full view of a non-abstract partial view must not be abstract. Seems to need change (can an incomplete type be completed by an abstract type? I think so.) 3.9.2(12.a.1/1): An AARM note that says that the rule against multiple dispatching applies to full views. (This was a ramification published in the defect reports.) Does this apply to incomplete types? Looks ugly either way: package Foo2 is ... private type Arg2; type Arg3; procedure Yikes (P : access Arg2; P2 : access Arg3); end Foo2; package body Foo2 is type Arg2 is tagged... type Arg3 is tagged... ... end Foo2; Is Yikes illegal? Where is the check made? Of course, if these were a tagged incomplete types it would be easier to check. So I think that this note is correct, and should stand, even for incomplete partial views. 3.9(2): Discusses a particular kind of partial view. No change needed. 3.4(24): Discusses more particular kinds of partial view. No change needed. 7.3.1(10-13): These notes describe operations of private partial views. They'd need to be qualified. But they're notes - no big deal. 3.2(2.p): Defines the glossary entry for "private type". Its "A partial view", it isn't exclusive. 3.2(4): A private type represents a partial view. No problem. Also, a partial view is composite. Humm. I don't think that is a problem, either, given how little can be done with an incomplete view. 3.2(5): Partial views may have discriminants. That's fine. Indeed, it looks like incomplete types were completely omitted from this section, which seems a bit weird. They're not that different from private types. 7.3(4): A private type declares a partial view. That probably would be OK without change, but it would be a bit confusing. Also, we would probably want to introduce the qualifier "private" here. The part about a partial view requiring a completion would have to change to "private partial view". 7.3(5): This is bracketed in the AARM to mean "redundant". It's also true of incomplete types - but a bit confusing. 7.3(6): This would have to be qualified as "private partial view" (no such thing as a limited incomplete type, nor are there any restrictions on completion). 7.3(7): The third sentence would have to be qualified. As would the following AARM notes (which I won't list here). 7.3(9): Incomplete types have the same rule; we could get rid of one of the copies. 7.3(10.a): AARM note, which is OK. 7.3(11): This already applies to incomplete types. Another rule we could get rid of. 7.3(12): Applies only to private types, would have to be qualified. 7.3(15): The first sentence applies to both; the rest (which probably ought to be split out) only apply to private types. 7.3(17): Specific uses of partial view; no change would be required. 7.3(18): Specific use of partial view; no change would be needed. 3.2.1(8): "partial and incomplete view". Humm. ************************************************************* From: Pascal Leroy Sent: Friday, February 27, 2004, 6:59 AM One of my actions items from the San Diego meeting reads: "Check that the current wording of AI-326 properly handles all of the examples discussed in previous meetings for AI-217." I have done this analysis and I am attaching a document that reviews all these examples (some of them were based on ancient alternatives of AI 217, so I had to adapt them to the "limited with" model). Bad news: the last examples from the Cupertino and Toulouse meetings demonstrate that we have reintroduced ripple effects. In essence, whenever we say "a dereference which is of an incomplete view is illegal if so-and-so" there is a ripple effect, because changing a "limited with" to a "with" (or vice-versa) at the place where the access type is declared can cause legality changes in distant places. Not sure how to fix this... -------- Cupertino package P is private type T is tagged; procedure Print (X : T); type T_Ptr is access all T; end P; private procedure P.Proc (Y : T_Ptr); procedure P.Proc (Y : T_Ptr) is Print (Y.all); -- Legal? (Yes.) end P.Proc; This example is legal because Y.all is of the incomplete view of T (3.10.1(11)), and the type of the formal parameter of Print is also the incomplete view of T, so the two match. Furthermore the defereference Y.all is not a prefix, so it is legal (3.10.1(10)). private procedure P.Proc2 (Y, Z : T_Ptr); procedure P.Proc2 (Y, Z : T_Ptr) is begin Z.all := Y.all; -- Legal? (No.) end P.Proc2; This example is illegal because Z.all and Y.all are both of the incomplete view of T, and an incomplete view is a limited view (3.10.1(11)). package Q is type T is tagged ...; end Q; with Q; package R is A : Q.T; end R; limited with Q; with R; procedure Proc3 (Y : access Q.T) is W : Q.T renames Y.all; -- Legal? (No.) begin R.A := Y.all; -- Legal? (Yes.) end Proc3; The renaming is illegal, because Q.T is the incomplete view, and an incomplete view is not permitted as the type of a renaming. The assignment is legal. R.A is of the full view of T, so it is not limited. The expected type of Y.all is T, and this expression is actually of the incomplete view of T, so name resolution works fine. with Q; package R2 is A : Q.T; procedure Store (I : out Q.T; J : P.T); end R2; limited with Q; with R2; procedure Proc4 (Y, Z : access Q.T) is begin R2.Store (Z.all, Y.all); -- Legal? (Yes.) end Proc4; This example is legal, because Y.all and Z.all are of the incomplete view of T, and in the call they are expected to be of type T, so name resolution works fine. limited with Q; package R3 is type T2_Ptr is access Q.T; end R3; with R3; procedure Proc5 is A, B : R3.T2_Ptr; begin A.all := B.all; -- Legal? (RIPPLE EFFECT HERE!) end Proc5; As written, the example is illegal because A.all and B.all are of the incomplete view of T, which is limited. However, if the "limited with Q" in R3 is changed to "with Q", A.all and B.all become nonlimited and the example becomes legal. -------- Vienna package Q is type T is tagged ... end Q; limited with Q; package R is type A is access T'Class; O : A; end R; with Q, R; package P is OC : Q.T; begin OC := R.O.all; -- Legal? (Yes.) end P; The type of OC is the full view of T. The type of R.O.all is the full view of T'Class because of the presence of the "with Q". The expected type of R.O.all is the full view of T, so the assignment is legal according to 8.6(23). type T is tagged; type A is access T; O : A; type T (D : Integer) is tagged ...; ... A.all.D ... -- Legal? (Yes.) In this example A.all occurs within the immediate scope of the completion of T, so it is of the full view of T, and therefore its use as a prefix is legal (and the discriminant D is visible). -------- Padua package Q is type T is ...; Z : T; end Q; limited with Q; package P is type Acc is access Q.T; X : Acc; end P; with Q; package A is package MyQ renames Q; end A; with P; with A; package R is ... P.X.all ... -- Legal? (Depends.) end R; P.X.all is of an incomplete type. It is not legal if used as a prefix or as the left-hand side of an assignment, but other uses are acceptable. At any rate, the legality of the dereference does not depend on the "with Q" in A. limited with Q; with P, A; package R2 is ... P.X.all ... -- Legal? (As above.) ... A.MyQ.Z ... -- Legal? (Yes.) ... Q.Z ... -- Legal? (No.) end R2; The legality of P.X.all is as in the example above. A.MyQ.Z is legal through the normal visibility rules. Q.Z is illegal because the full view of Q is hidden from all visibility within the scope of the "limited with Q", regardless of the presence of MyQ in A. with Q; package QE2 renames Q; with QE2; limited with Q; -- Illegal. with P, A; package R3 is ... P.X.all ... -- Legal? (Irrelevant.) ... QE2.Z ... -- Legal? (Irrelevant.) ... Q.Z ... -- Legal? (Irrelevant.) end R3; In this case the "limited with Q" is illegal because it occurs in the same context_clause as a nonlimited_with_clause which mentions the same library_item (the "with QE2" mentions Q). -------- Toulouse package Q is type T is ...; procedure Proc (Param : T); X : T; end Q; with Q; package P is package R renames Q; end P; with P; limited with Q; package PP is Obj1 : Q.T; -- Legal? (No.) Obj2 : P.R.T; -- Legal? (No.) end PP; The first declaration is illegal because Q.T is the incomplete view of T, and an object_declaration is not a legal use for an incomplete view. The second declaration is illegal because, within the scope of the "limited with Q", the full view of Q is hidden from all visibility. Therefore, P.R.T is the incomplete view of T, and it cannot be used in an object declaration. with Q; package R1 is subtype Foo is Q.T; type Rec is private; Z : Q.T; function "=" (A, B : Q.T) return Boolean renames Q."="; private type Rec is record F : Q.T; end record; end R1; with R1; limited with Q; package S1 is X : R1.Rec; -- Legal? (Yes.) Y : Q.T; -- Legal? (No.) Z : R1.Foo; -- Legal? (Yes.) Maybe : Boolean := R."=" (R1.Z, X.F); -- Legal? (Would be if F were visible.) end S1; The declaration of X is trivially legal: even though the full view of Q is hidden from all visibility, there is no reference to that view in the declaration of X. The declaration of Y is illegal precisely because the full view of Q is hidden from all visibility. The declaration of Z is legal for essentially the same reason as that of X. And the call to "=" in the declaration of Maybe would be legal if the component F were visible, because again there is no name resolution involving a view of Q. package Q3 is type T is tagged record Comp : Natural; end record; procedure Proc (Param : T); X : T; end Q3; limited with Q3; -- (0) package P3 is type Acc_Inc is access Q3.T; X : Acc_Inc; end P3; with Q3; -- (1) package R3 is M : Q3.T := P3.X.all; -- Legal? (Yes.) procedure Proc (X : Q3.T); end R3; with P3, R3; procedure S3 is N : Natural := P3.X.all.Comp; -- Legal? (RIPPLE EFFECT.) begin R3.Proc (P3.X.all); -- Legal? (Yes.) end S3; The dereference P3.X.all in the declaration of M is legal: P3.X.all is of the incomplete view of Q3.T, and its expected type is the full view of Q3.T, so the two match. If the with_clause marked (1) were changed to a limited_with_clause, the declaration of M would become illegal because Q3.T would be a limited type. The dereference P3.X.all.Comp in the declaration of N is illegal because P3.X.all is of the incomplete view of Q3.T, so it cannot be used as a prefix. However, if the with_clause marked (0) were changed to a nonlimited_with_clause, the declaration of N would become legal. YARE! (Yet Another Ripple Effect.) The call to R3.Proc is legal because P3.X.all is of the incomplete view of T, and its expected type is T. Note that this is true even if the with_clause marked (1) is changed to a limited_with_clause (that's necessary to prevent ripple effects). -------- Sydney limited with Depts; package Emps is type Employee is private; procedure Assign (E : Employee; D : Depts.Department); type Dept_Ptr is access Depts.Department; end Emps; with Emps; procedure Recruit (D : Emps.Dept_Ptr; E : Emps.Employee) is begin Emps.Assign (E, D.all); -- Legal? (Yes.) end Recruit; Here D.all is of the incomplete view of Depts.Department, and its expected type is the incomplete view of Depts.Department, so the two match and the call is legal. ************************************************************* From: Randy Brukardt Sent: Friday, February 27, 2004, 7:35 PM OK. Let's start with the easy stuff: --- Cupertino: with Q; package R2 is A : Q.T; procedure Store (I : out Q.T; J : P.T); end R2; There's no P visible here. I think that is supposed to be Q.T, not P.T. --- Toulouse: with R1; limited with Q; package S1 is X : R1.Rec; -- Legal? (Yes.) Y : Q.T; -- Legal? (No.) Z : R1.Foo; -- Legal? (Yes.) Maybe : Boolean := R."=" (R1.Z, X.F); -- Legal? (Would be if F were visible.) end S1; There's no R visible here. I think that is supposed to be R1."=". --- Now, the not so easy stuff: > Bad news: the last examples from the Cupertino and Toulouse meetings > demonstrate that we have reintroduced ripple effects. In essence, > whenever we say "a dereference which is of an incomplete view is illegal > if so-and-so" there is a ripple effect, because changing a "limited > with" to a "with" (or vice-versa) at the place where the access type is > declared can cause legality changes in distant places. I'm not sure that this is a problem that could be solved without making "limited with" useless. It's hard to imagine any other result. Let me explain. Here's the example that causes trouble: limited with Q; -- or with Q; package R3 is type T2_Ptr is access Q.T; end R3; with R3; procedure Proc5 is A, B : R3.T2_Ptr; begin A.all := B.all; -- Legal? (No was written, Yes if "with Q") - RIPPLE EFFECT HERE! end Proc5; Changing "with Q" to "limited with Q" essentially is changing the declaration of the type Q.T from a complete tagged type to an incomplete type. It cannot be surprising that changing a declaration causes changes in the legality elsewhere. Certainly if you made such a change explicitly in the program text, you'd expect there to be a recompile/recheck. (Think of changing a visible non-limited type to a private limited type. Would you be surprised? Thought not.) There are many examples of this sort of "ripple effect" in Ada, so I don't think this is a problem. In any case, changing from limited with to with only makes illegal code legal. That's hardly a problem in practice (who has thousands of lines of illegal code sitting around waiting for a change somewhere to make it legal??). Changing in the other direction is obviously dangerous and rarely necessary (only if the design needs major changes); you're removing a lot of capability, and no one should be surprised if that makes code illegal. Such changes need to be avoided anyway - hopefully Ada programming guides will make this clear. This doesn't seem to be a huge implementation problem, either. Changing limited with <-> with is essentially the same as any other significant edit to a spec. -- it requires rechecking all dependent units. If you need specific entities to change, you'd have to scan the spec. and mark all of the entities that depend on the withed package (Q) as changed, so that they're rechecked. Note that this cannot happen unless the with clause is "needed"; that is that it is referenced somewhere in the spec. of the package (or its children). So, this can't happen by the adding or removing of otherwise unneeded with clauses -- which is the maintenance problem that we wish to avoid by avoiding ripple effects. (If the with clause was totally removed, you'd get a local error - which is not a ripple effect.) The Toulouse example is precisely the same effect (changing a declaration of a "needed" item). The only way that this could be fixed would be to insure that precisely the same properties exist for a type whether it is complete or incomplete. (That is, avoid the possibility of a legality change.) But that way lies madness - either massive incompatibility (by removing capabilities from full types) or it's unimplementable (because we'd be accessing things not yet defined!). So, I don't think that there is a problem worth fixing (or able to fix, for that matter) here. This is a very mild ripple effect (if it is one at all; I wasn't able to find a definition of the term in a quick search). ************************************************************* From: Pascal Leroy Sent: Tuesday, March 2, 2004, 4:14 AM After giving it more thought, I agree with your analysis. In fact, I think it would fall out naturally from our incremental compilation mechanism. It might also be good to add to the !discussion section of the AI an example of this pseudo-ripple effect, together with an explanation of why this is not really problematic. ************************************************************* From: Randy Brukardt Sent: Tuesday, March 2, 2004, 5:58 PM I don't think this issue has much to do with 326; it's really all about "limited with" (not incomplete types per-se). So any change should be in that AI. *************************************************************