Version 1.1 of ai12s/ai12-0030-1.txt

Unformatted version of ai12s/ai12-0030-1.txt version 1.1
Other versions for file ai12s/ai12-0030-1.txt

!standard 12.5.1(20/3)          12-06-06 AI12-0030-1/01
!class binding interpretation 12-06-06
!status work item 12-06-06
!status received 12-05-11
!priority Low
!difficulty Medium
!subject Formal derived types and attribute availability
!summary
**TBD.
!question
Consider this example:
procedure Foo
(S : not null access Ada.Streams.Root_Stream_Type'Class) is
type Lim is limited null record;
type Dlim is new Lim; -- does not inherit Lim'Read
procedure Read_Lim (Stream : not null access Ada.Streams.Root_Stream_Type'Class; Item : out Lim) is null;
for Lim'Read use Read_Lim; Lim_Var : Lim; Dlim_Var : Dlim;
generic type Flim is new Lim; procedure G; procedure G is Flim_Var : Flim; begin Flim'Read (S, Flim_Var); -- Legal? (Yes.) end G; procedure I is new G (Dlim);
begin Lim'Read (S, Lim_Var); Dlim'Read (S, Dlim_Var); -- Illegal. I; end;
Is the use of Flim'Read legal? Should it be? If it is legal, are the dynamic semantics well defined?
Assuming that attribute availability is a "characteristic", it seems that 12.5.1(20/3) applies and the use within the generic body is legal. This means we need to define the meaning of the corresponding construct in the instance.
The "even if it is never declared" wording in 12.5.1(21/3) handles a similar case, the case where a generic formal type promises a primitive operation that the corresponding actual type lacks. Do may need to add some analogous wording to handle this case? (***???***).
!recommendation
** TBD.
!wording
** TBD.
!discussion
The answer to the legality question is clear: characteristics of all sorts come from the ancestor type, not the actual type, so of course the attribute is legal and it will call the attribute of the ancestor type.
What's not so clear is how to get this result. "Availability of stream attributes" is not a characteristic of a type (at least it is not listed in 3.4, which is the definition of a "characteristic". So the existing rules don't cover this case. There seem to a number of possibilities:
(1) Just add a TBH noting that "availability" of stream attributes is treated in the same way as a characteristic of the type. It's very rare to see a derived untagged formal type, as such types can only match derived untagged types -- which is very limited, especially given that untagged derivations themselves aren't that common (it's usually better to declare a new type).
If we go this route, this AI should be written as a Ramification.
(2) Add "availability of stream attributes" to the list of characteristics found in 3.4 (probably with a new bullet at 3.4(15)). Then then existing rule 12.5.1(20/3) and various rules in 7.3.1 would handle this case without further changes. However, there is a risk that this would then interfere with the availability definitions in 13.13.2; most likely those would need to be modified. [I didn't check this or determine if there would be any compatibility problems with this approach - Editor]
(3) Add a new rule after 12.5.1(20/3) specifically to say that "availability of stream attributes" is the same as that of the ancestor type, and the stream attribute called is that of the ancestor type. This is the safest solution, but it probably requires the most wording.
!ACATS test
** TBD.
!appendix

From: Steve Baird
Sent: Friday, May 11, 2012  5:27 PM

Consider this example:

   procedure Foo
     (S : not null access Ada.Streams.Root_Stream_Type'Class) is
      type Lim is limited null record;

      type Dlim is new Lim; -- does not inherit Lim'Read

      procedure Read_Lim
        (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
         Item   : out Lim) is null;

      for Lim'Read use Read_Lim;
      Lim_Var : Lim;
      Dlim_Var : Dlim;

      generic
         type Flim is new Lim;
      procedure G;
      procedure G is
         Flim_Var : Flim;
      begin
         Flim'Read (S, Flim_Var); -- legal?
      end G;
      procedure I is new G (Dlim);
   begin
      Lim'Read (S, Lim_Var);
      Dlim'Read (S, Dlim_Var); -- illegal
      I;
   end;

Is the use of Flim'Read legal? Should it be?
If it is legal, are the dynamic semantics well defined?

Assuming that attribute availability is a "characteristic", it seems that
12.5.1(20/3) applies and the use within the generic body is legal. This means we
need to define the meaning of the corresponding construct in the instance.

The "even if it is never declared" wording in 12.5.1(21/3) handles a similar
case, the case where a generic formal type promises a primitive operation that
the corresponding actual type lacks. We may need to add some analogous wording
to handle this case (or at least a TBH note if this seems like too much of a
corner case).

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

From: Tucker Taft
Sent: Sunday, May 13, 2012  10:08 PM

> generic
> type Flim is new Lim;
> procedure G;
> procedure G is
> Flim_Var : Flim;
> begin
> Flim'Read (S, Flim_Var); -- legal?

Yes.

> end G;
> procedure I is new G (Dlim);
> begin
> Lim'Read (S, Lim_Var);
> Dlim'Read (S, Dlim_Var); -- illegal

True.

> I;
> end;
>
> Is the use of Flim'Read legal? Should it be?

Yes, just like all operations for non-tagged types.

> If it is legal, are the dynamic semantics well defined?

Yes, since the semantics are always to use the operation of the specified
ancestor type if non-tagged.  We have now made "=" a special case, but all other
operations come from the ancestor type.

> Assuming that attribute availability is a "characteristic", it seems
> that 12.5.1(20/3) applies and the use within the generic body is
> legal. This means we need to define the meaning of the corresponding
> construct in the instance.
>
> The "even if it is never declared" wording in 12.5.1(21/3) handles a
> similar case, the case where a generic formal type promises a
> primitive operation that the corresponding actual type lacks. We may
> need to add some analogous wording to handle this case (or at least a
> TBH note if this seems like too much of a corner case).

I don't really see this as a very special case.
For non-tagged types, unless you explicitly import the operation (or it is
equality), you always use the operations of the specified ancestor, even if they
are overridden in the actual type.

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

From: Steve Baird
Sent: Monday, May 14, 2012  12:31 PM

> I don't really see this as a very special case.
> For non-tagged types, unless you explicitly import the operation (or
> it is equality), you always use the operations of the specified
> ancestor, even if they are overridden in the actual type.

If I understand you correctly, you are talking about the following 12.5.1(21/3)
rule:

   In an instance, the copy of such an implicit declaration of a
   primitive subprogram of a formal derived type declares a view of the
   corresponding primitive subprogram of the ancestor or progenitor of
   the formal derived type, even if this primitive has been overridden
   for the actual type and even if it is never declared for the actual
   type.

and (in passing) the 3.4(17/2) rule that begins
   For each user-defined primitive subprogram (other than a user-defined
   equality operator - see below) of the parent type ...

Note that these rules talk about primitive subprograms, not primitive
operations. I think that means that they don't apply to the question I raised.

Note that it wouldn't change the example in any essential way if we introduced a
nested package so that the procedure Read_Lim is not a primitive operation of
type Lim, as in

      type Lim is limited null record;

      type Dlim is new Lim; -- does not inherit Lim'Read

      package Prevent_Primitiveness is
        procedure Read_Lim
          (Stream : not null access Ada.Streams.Root_Stream_Type'Class;
           Item   : out Lim) is null;
      end Prevent_Primitiveness;

      for Lim'Read use Prevent_Primitiveness.Read_Lim;

This is not a question about primitive subprograms.

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

From: Tucker Taft
Sent: Monday, May 14, 2012  12:56 PM

> ...
> Note that these rules talk about primitive subprograms, not primitive
> operations. I think that means that they don't apply to the question I
> raised.

You are right in the letter of the law, but I think the intent was to include
all overrideable operations. Note that the streaming operations of tagged types
are considered dispatching operations, so that would mean that we are already
treating them pretty similarly to primitives.

In general, we have not presumed any significant "descriptor" needed to
accompany formal derived non-tagged types (presuming you have some amount of
sharing), since essentially all of the properties inside the generic are
determined by the specified ancestor type.  I agree we should clarify this, but
I have no doubt which way it should be clarified. Your example is a good
indication of why for non-tagged types we should carry this same principle
through to the streaming operations, and use the operations of the specified
ancestor type inside the generic.

> ... This is not a question about primitive subprograms.

Agreed, but it is a question about something which is the "moral equivalent" of
a primitive, and I believe we should clarify the RM that they should follow the
same rules.

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

From: Steve Baird
Sent: Monday, May 14, 2012  1:24 PM

Sounds good to me.

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

Questions? Ask the ACAA Technical Agent