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

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

!standard 3.9.3(8)          08-04-18 AI05-0073-1/04
!standard 3.9.3(10)
!standard 6.5(8/2)
!class binding interpretation 07-10-24
!status Amendment 2012 08-11-26
!status WG9 Approved 08-06-20
!status ARG Approved 9-0-0 06-11-11
!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.
For a tagged type declared in a visible part, a primitive function with a controlling access result cannot be declared in the private part.
!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) 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 generic 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). Should the generic function 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?
(3) 3.9.3(10) has rules that prevent a visible tagged type from having "hidden" routines that would require overriding for a derived type. The Amendment changed the rules for "require overriding" [3.9.3(4-6/2)] to include functions with controlling access results. But it doesn't make a corresponding change in 3.9.3(10). What is supposed to happen when such types are derived?
!recommendation
(See Summary.)
!wording
Add to the end of 3.9.3(8):
If a function has an access result type designating 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.
Modify 3.9.3(10) as follows:
For an abstract type declared in a visible part, an abstract primitive subprogram shall not be declared in the private part, unless it is overriding an abstract subprogram implicitly declared in the visible part. For a tagged type declared in a visible part, a primitive function with a controlling result {or a controlling access result} shall not be declared in the private part, unless it is overriding a function implicitly declared in the visible part.
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 tagged 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 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 3.9.3(8)
Replace the paragraph:
The type of an aggregate, or of an object created by an object_declaration or an allocator, or a generic formal object of mode in, shall not be abstract. The type of the target of an assignment operation (see 5.2) shall not be abstract. The type of a component shall not be abstract. If the result type of a function is abstract, then the function shall be abstract.
by:
The type of an aggregate, or of an object created by an object_declaration or an allocator, or a generic formal object of mode in, shall not be abstract. The type of the target of an assignment operation (see 5.2) shall not be abstract. The type of a component shall not be abstract. If the result type of a function is abstract, then the function shall be abstract. If a function has an access result type designating 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.
!corrigendum 3.9.3(10)
Replace the paragraph:
For an abstract type declared in a visible part, an abstract primitive subprogram shall not be declared in the private part, unless it is overriding an abstract subprogram implicitly declared in the visible part. For a tagged type declared in a visible part, a primitive function with a controlling result shall not be declared in the private part, unless it is overriding a function implicitly declared in the visible part.
by:
For an abstract type declared in a visible part, an abstract primitive subprogram shall not be declared in the private part, unless it is overriding an abstract subprogram implicitly declared in the visible part. For a tagged type declared in a visible part, a primitive function with a controlling result or a controlling access result shall not be declared in the private part, unless it is overriding a function implicitly declared in the visible part.
!corrigendum 6.5(8/2)
Insert after the paragraph:
If the result type of a function is a specific tagged type, the tag of the return object is that of the result type. If the result type is class-wide, the tag of the return object is that of the value of the expression. A check is made that the accessibility level of the type identified by the tag of the result is not deeper than that of the master that elaborated the function body. If this check fails, Program_Error is raised.
the new paragraph:
If the result subtype of the function is defined by an access_definition designating a specific tagged 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.
!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: Tucker Taft
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.

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

From: Randy Brukardt
Sent: Thursday, October 25, 2007  12:42 PM

I opened the test objectives for 3.9.3 in order to check that I had remembered
to retest them for interface type (I had), and happened to notice another
problem.

3.9.3(10) has rules that prevent a visible tagged type from having "hidden"
routines that would require overriding for a derived type. We changed the rules
for "require overriding" [3.9.3(4-6/2)] to include functions with controlling
access results. But we didn't make a corresponding change in 3.9.3(10). Oops.

I'll add this to the already open AI for abstract screwups, AI05-0073-1.

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

Questions? Ask the ACAA Technical Agent