Version 1.5 of ais/ai-00326.txt

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

!standard 03.10.01 (02)          03-07-15 AI95-00326/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 J.11 (00)
!class amendment 02-02-06
!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 Tagged 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 them 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 for a class-wide tagged incomplete formal 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
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 keyword TAGGED, then a type_declaration that completes it shall declare a tagged type.
Replace 3.10.1(5-9):
A name that denotes an incomplete_type_declaration 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 defining the subtype of a parameter or result of an access_to_subprogram_definition;
* as the subtype_mark in an access_definition;
A name that denotes an incomplete_type_declaration that includes the keyword TAGGED may also be used as follows:
* 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 above.
Replace 3.10.1(10):
An incomplete_type_declaration declares an incomplete type and its
Replace 3.10.1(11):
A dereference (whether implicit or explicit -- see 4.1) shall not be of an incomplete type, unless it is directly used as an actual parameter where the expected type is a class-wide tagged incomplete type.
Add J.11:
J.11 The Class Attribute of Non-tagged 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.).
Similarly, we are not allowing an incomplete tagged type to be dereferenced (and thus used as an actual parameter) in most cases, because doing so may require the reading of the tag and/or discriminants, whose positions are not known. However, we are allowing such a use as an actual parameter of a class-wide tagged incomplete type, as such objects are passed by reference with no need to read any portion of them.
For instance:
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); -- OK. 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; it can see the completing type).
!example
!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];
!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 keyword tagged, then a type_declaration that completes it shall declare a tagged type. If an incomplete_type_declaration has a known_discriminant_part, then a 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 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_type_declaration may be used as follows:
!corrigendum 3.10.1(8)
Replace the paragraph:
by:
A name that denotes an incomplete_type_declaration that includes the keyword tagged may also be used as follows:
!corrigendum 3.10.1(9)
Replace the paragraph:
by:
!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 dereference (whether implicit or explicit -- see 4.1) shall not be of an incomplete type, unless it is directly used as an actual parameter where the expected type is a class-wide tagged incomplete type.
!corrigendum 3.10.1(11)
Replace 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.
by:
An incomplete_type_declaration declares an incomplete type and its first subtype; the incomplete type is tagged if the keyword tagged appears; the first subtype is unconstrained if a known_discriminant_part appears.
!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 and is not a type stub, 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.

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


Questions? Ask the ACAA Technical Agent