Version 1.1 of ai05s/ai05-0073-1.txt

Unformatted version of ai05s/ai05-0073-1.txt version 1.1
Other versions for file ai05s/ai05-0073-1.txt

!standard 3.9.3(8/2)          07-10-24 AI05-0073-1/01
!class binding interpretation 07-10-24
!status work item 07-10-24
!status received 07-10-09
!priority Medium
!difficulty Medium
!qualifier Error
!subject Questions about functions returning abstract types
!summary
generic functions cannot have abstract result types or access result types designating an abstract type.
A function with an access result type designating an abstract type must be abstract.
There is a check that a function with an access result type designating a specific tagged type returns a value which designates a value of that specific tagged type.
!question
(1) Consider:
generic type T (<>) is abstract tagged private; function Gft (X : T) return T;
This appears to be legal, because a generic function is not a function and 3.9.3(8/2) only talks about functions. Moreover, it is possible to write a body for this generic by using an abstract formal subprogram. But it is not useful.
Also note that a similar package:
generic type T (<>) is abstract tagged private; package GP is function Ft (X : T) return T; end GP;
Is illegal because it violates 3.9.3(8/2). Should the generic also be illegal? (Yes.)
(2) Consider:
package Pkg is type T (<>) is abstract tagged private; function Ft (X : in T) return access T; end Pkg;
This is legal, as "access T" is surely not abstract. However, this function can only return null or values designating types derived from T. If the latter is meant, it would have been better to write:
function Ft (X : in T) return access T'Class;
Also note that this function has a controlling access result. It would be obnoxious for it to return a result designating an object with a different type. For instance, if we have:
function Empty return access Set; function Union(S1, S2 : access Set) return access Set; procedure Assign(S1, S2 : access Set); ... Assign(X'access, Union(Empty, Y'access));
we don't want Empty returning a value that designates some type derived from Set, as that would imply that we should have a dispatching tag check failure -- but this call is statically bound!
What is the intent here?
!recommendation
(See Summary.)
!wording
Add to the end of 3.9.3(8/2):
If a function has an access result type designated an abstract type, then the function shall be abstract. A generic function shall not have an abstract result type or an access result type designating an abstract type.
Add a new paragraph after 6.5(8/2):
If the result subtype of the function is defined by an access_definition designating a specific access type T, a check is made that the result value is null or the tag of the object designated by the result value identifies T. Constraint_Error is raised if this check fails.
!discussion
In order to fix the tag-indeterminate dispatching problem, we require that the designated type of the result of such a function has the tag of the designated specific tagged type.
This tag check on return is similar to the one that was be made for return-by-reference types in Ada 95. This makes sense, since access result types are essentially a replacement for that functionality.
With this tag check, the only thing that can be returned from a function with an access result type designating an abstract type is null. That's not useful, so we make such functions illegal, just like functions that directly return an abstract type.
--!corrigendum 7.6.1(17.1/1)
!ACATS Test
!appendix

From: Randy Brukardt
Sent: Tuesday, October 9, 2007  2:25 PM

I received a complaint about a test case in a new ACATS test. The example
was

     generic
         type T (<>) is abstract tagged private;
     function Gft (X : T) return T;

The complaint said that this violates 3.9.3(8): "If the result type of a
function is abstract, then the
function shall be abstract". Seems straightforward.

But then in the interests of completeness he continued...

"Even if you argued that it 3.9.3(8) didn't apply because a "generic
function" is not a function, it seems like this ought to be illegal anyway,
because I don't see how you could legally write a body for
this generic function since you couldn't write a legal RETURN statement,
right?"

That makes this a *whole lot* more interesting.

First of all, he surely is right that a "generic function" is not a
"function". So it doesn't appear that 3.9.3(8) applies directly (although it
does apply to any instances).

Second, his contention that you can't write a body is wrong; you could use
an abstract formal subprogram to provide the value for the return statement.
Consider:

    generic
        type T (<>) is abstract tagged private;
        with function Constructor (X : in T) return T is abstract;
    function Gft (X : in T) return T;

    function Gft (X : in T) return T is
    begin
        return Constructor(X);
    end Gft;

Since Constructor is dispatching, it is legal for it to be abstract. So this
doesn't seem to violate any rules. But it does seem to be useless (you could
just call Constructor directly), and you'd have to instantiate with a
specific type (else the instantiation would violate 3.9.3(8)), and
Constructor would have to be primitive. It seems like it would be a lot of
work to implement for little gain, especially since only concrete types
would work
(in which case, why the type is declared abstract has to be questioned).

Third, note that a similar package is clearly illegal:

    generic
        type T (<>) is abstract tagged private;
        with function Constructor (X : in T) return T is abstract;
    package GPkg is
       function Ft (X : in T) return T; -- Error: Not abstract.
    end GPkg;

...as Ft is clearly a function.

So it's pretty clear that 3.9.3(8) should apply to generic functions;
probably we just need to clarify 3.9.3(8) to make that clear. Or is there
some way to say that this applies anyway?

---------

In thinking about ways to fix the test, the first thought that came to mind
was to make the result an anonymous access type:

     generic
         type T (<>) is abstract tagged private;
     function Gft (X : T) return access T;

or the more usual case:

    generic
        type T (<>) is abstract tagged private;
    package GPkg is
       function Ft (X : in T) return access T;
    end GPkg;

and even the basic case:

    package Pkg is
       type T (<>) is abstract tagged private;
       function Ft (X : in T) return access T;
    end Pkg;

That clearly does not violate 3.9.3(8): "access T" is not abstract. And the
subprogram is still primitive for "access T".

But should we be allowing this? You cannot get an object of type access T,
because allocators and object declarations are illegal for T. You could
convert an access to class-wide to this result type, but the tag check would
necessarily fail. And you could return null, of course. But what good are
those possibilities?

So it looks like any legitimate use is impossible. Given that we want to
detect errors early, it seems to me that 3.9.3(8) should also apply to
access result types that designate abstract types.

Most likely, this is something that we didn't think about when we added
access result types. I don't see anything about abstract operations in
AI-318-2 or AI-416. Thoughts?

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

From: Randy Brukardt
Sent: Tuesday, October 9, 2007  3:59 PM

I think it should be illegal, because inside the generic
body, "Gft" denotes the current instance, which in
this case would violate the rule that a function not
have an abstract result type.

As far as access T, converting from access T'Class to
access T doesn't involve a tag check, and there is
no tag check associated with returning a value of
type "access T."

I *do* think there might be a problem in general here
in the general vicinity of tag-indeterminate calls on
functions with result type "access T."  There is
a normal assumption that tag indeterminate calls
are guaranteed to return an object whose tag matches
that of the controlling tag used to perform the
dispatching.  Since we don't do a tag check on
functions returning access T, that seems like a
bit of a problem:

    function Empty return access Set;
    function Union(S1, S2 : access Set) return Access Set;
    procedure Assign(S1, S2 : access Set);
       ...
    Assign(X'Access, Union(Empty, Y'Access));

Is there any guarantee that the Empty associated with
type Hashed_Set, say, returns a value designating an object
with tag identifying Hashed_Set?

If we were to require that functions with return type
"access T" (with T tagged) actually return a value
designating an object with tag = T'Tag, then it
would make sense to disallow non-abstract functions
returning access <abstract-type>.

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

From: Randy Brukardt
Sent: Tuesday, October 9, 2007  7:06 PM

...
> As far as access T, converting from access T'Class to
> access T doesn't involve a tag check, and there is
> no tag check associated with returning a value of
> type "access T."

Humm, I guess you are right. I was thinking that T'Class => T involved a tag
check, but upon rereading the manual, I see it does not.

As you imply below, though, returning a tagged object from a function of a
specific type implies a tag check, and the fact that we don't have one here
seems to imply that something is wrong.

> I *do* think there might be a problem in general here
> in the general vicinity of tag-indeterminate calls on
> functions with result type "access T."  There is
> a normal assumption that tag indeterminate calls
> are guaranteed to return an object whose tag matches
> that of the controlling tag used to perform the
> dispatching.  Since we don't do a tag check on
> functions returning access T, that seems like a
> bit of a problem:
>
>     function Empty return access Set;
>     function Union(S1, S2 : access Set) return Access Set;
>     procedure Assign(S1, S2 : access Set);
>        ...
>     Assign(X'Access, Union(Empty, Y'Access));
>
> Is there any guarantee that the Empty associated with
> type Hashed_Set, say, returns a value designating an object
> with tag identifying Hashed_Set?

No. In any case, it is inconsistent with directly returned objects.

I'm not sure if that is a real problem or not; I suppose it again comes down
to tag checks. Compilers don't expect to have to make a tag check on Empty,
but this example seems to imply one is necessary on the call. And that's
different than regular checks. We could move the check into the function
(there used to be a similar check for limited types in Ada 95; it was
removed in the Amendment since build-in-place eliminated the need). Not sure
if that is much of a difference; the only issue seems to be where the check
is done. (And doing the check at the call site is likely to allow more
flexibility in non-dispatching contexts.) But I'm not sure what makes sense
here.

> If we were to require that functions with return type
> "access T" (with T tagged) actually return a value
> designating an object with tag = T'Tag, then it
> would make sense to disallow non-abstract functions
> returning access <abstract-type>.

Yes, that makes sense.

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

From: Tucker Taft
Sent: Wednesday, October 10, 2007  12:42 PM

> I'm not sure if that is a real problem or not; I suppose it again comes down
> to tag checks. Compilers don't expect to have to make a tag check on Empty,
> but this example seems to imply one is necessary on the call. And that's
> different than regular checks. We could move the check into the function
> (there used to be a similar check for limited types in Ada 95; it was
> removed in the Amendment since build-in-place eliminated the need). Not sure
> if that is much of a difference; the only issue seems to be where the check
> is done. (And doing the check at the call site is likely to allow more
> flexibility in non-dispatching contexts.) But I'm not sure what makes sense
> here.

I think we need to reintroduce a tag check on return
for functions with return-type "access T" if T is
tagged.  It would be very similar to the check
we had in Ada 95 on return-by-reference.  This also
makes sense because we have to some extent said that
what was "return Lim_Type" in Ada 95 becomes
"return access Lim_Type" in Ada 2005.  Hence, one should
not be surprised if the tag check is carried over.

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

Questions? Ask the ACAA Technical Agent