Version 1.16 of ais/ai-00326.txt

Unformatted version of ais/ai-00326.txt version 1.16
Other versions for file ais/ai-00326.txt

!standard 03.02 (04)          05-01-31 AI95-00326/09
!standard 03.02 (05)
!standard 03.02.01 (08)
!standard 03.07 (01)
!standard 03.09 (30)
!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 represents its complete definition. An incomplete or partial view is considered a composite type, even if the full view is not.
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:
The declaration of a full type also declares the full view of the 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.
Delete 3.9(30). The second sentence is obsolescent, and the first part is too obscure to discuss.
Replace 3.10.1(2):
incomplete_type_declaration ::= type defining_identifier [discriminant_part] [is tagged];
Static Semantics
An incomplete_type_declaration declares an incomplete view of a type, and its first subtype; the first subtype is unconstrained if a 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 within the immediate scope of the completion of T, or
* it occurs within 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).
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 shall only 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.
No other uses of a @fa<name> that denotes an incomplete view of a type are allowed.
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).
Delete 3.10.1(11) (we moved it above)
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(E : 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)
Replace the paragraph:
The composite types are the record types, record extensions, array types, task types, and protected types. A private type or private extension represents a partial view (see 7.3) of a type, providing support for data abstraction. A partial view is a composite type.
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 represents its complete definition. An incomplete or partial view is considered a composite type, even if the full view is not.
!corrigendum 3.2(5)
Replace the paragraph:
Certain composite types (and partial views thereof) have special components called discriminants whose values affect the presence, constraints, or initialization of other components. Discriminants can be thought of as parameters of the type.
by:
Certain composite types (and views thereof) have special components called discriminants 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)
Replace the paragraph:
A named type that is declared by a full_type_declaration, or an anonymous type that is defined as part of declaring an object of the type, is called a full type. The type_definition, task_definition, protected_definition, or access_definition that defines a full type is called a full type definition. Types declared by other forms of type_declaration are not separate types; they are partial or incomplete views of some full type.
by:
A named type that is declared by a full_type_declaration, or an anonymous type that is defined as part of declaring an object of the type, is called a full type. The declaration of a full type also declares the full view of the type. The type_definition, task_definition, protected_definition, or access_definition that defines a full type is called a full type definition. Types declared by other forms of type_declaration are not separate types; they are partial or incomplete views of some full type.
!corrigendum 3.7(1)
Replace the paragraph:
A composite type (other than an array type) can have discriminants, which parameterize the type. A known_discriminant_part 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 unknown_discriminant_part 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.
by:
A composite type (other than an array type) can have discriminants, which parameterize the type. A known_discriminant_part 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 unknown_discriminant_part 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.9(30)
Delete the paragraph:
65 If S denotes an untagged private type whose full type is tagged, then S'Class is also allowed before the full type definition, but only in the private part of the package in which the type is declared (see 7.3.1). Similarly, the Class attribute is defined for incomplete types whose full type is tagged, but only within the library unit in which the incomplete type is declared (see 3.10.1).
!corrigendum 3.10.1(2)
Replace the paragraph:
incomplete_type_declaration ::= type defining_identifier [discriminant_part];
by:
incomplete_type_declaration ::= type defining_identifier [discriminant_part] [is tagged];
Static Semantics
An incomplete_type_declaration declares an incomplete view of a type, and its first subtype; the first subtype is unconstrained if a 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).
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:
In these cases, the dereference has the full view of T.
!corrigendum 3.10.1(4)
Replace the paragraph:
If an incomplete_type_declaration has a known_discriminant_part, then a full_type_declaration that completes it shall have a fully conforming (explicit) known_discriminant_part (see 6.3.1). If an incomplete_type_declaration has no discriminant_part (or an unknown_discriminant_part), then a corresponding full_type_declaration is nevertheless allowed to have discriminants, either explicitly, or inherited via derivation.
by:
If an incomplete_type_declaration includes the reserved word tagged, then a full_type_declaration that completes it shall declare a tagged type. If an incomplete_type_declaration has a known_discriminant_part, then a full_type_declaration that completes it shall have a fully conforming (explicit) known_discriminant_part (see 6.3.1). If an incomplete_type_declaration has no discriminant_part (or an unknown_discriminant_part), then a corresponding full_type_declaration is nevertheless allowed to have discriminants, either explicitly, or inherited via derivation.
!corrigendum 3.10.1(5)
Replace the paragraph:
The only allowed uses of a name that denotes an incomplete_type_declaration are as follows:
by:
A name that denotes an incomplete view of a type may be used as follows:
!corrigendum 3.10.1(7)
Delete the paragraph:
!corrigendum 3.10.1(8)
Replace the paragraph:
by:
If such a name denotes a tagged incomplete view, it may also be used:
!corrigendum 3.10.1(9)
Replace the paragraph:
by:
If such a name occurs within the list of declarative_items containing the completion of the incomplete view, it may also be used:
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.
No other uses of a name that denotes an incomplete view of a type are allowed.
!corrigendum 3.10.1(10)
Replace the paragraph:
A dereference (whether implicit or explicit -- see 4.1) shall not be of an incomplete type.
by:
A prefix shall not be of an incomplete view.
!corrigendum 3.10.1(11)
Delete the paragraph:
An incomplete_type_declaration declares an incomplete type and its first subtype; the first subtype is unconstrained if a known_discriminant_part appears.
!corrigendum 10.1.1(12)
Insert after the paragraph:
A library_unit_declaration or a library_unit_renaming_declaration is private if the declaration is immediately preceded by the reserved word private; it is otherwise public. A library unit is private or public according to its declaration. The public descendants of a library unit are the library unit itself, and the public descendants of its public children. Its other descendants are private descendants.
the new paragraphs:
For each library package_declaration in the environment, there is an implicit declaration of a limited view of that library package. The limited view of a package contains:
!corrigendum 13.1(11/1)
Replace the paragraph:
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.
by:
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)
Insert new clause:
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.
!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.

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


Questions? Ask the ACAA Technical Agent