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

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

!standard 6.5(5.2/2)          07-11-11 AI05-0032-1/02
!standard 6.5(5.3/2)
!standard 6.5(5.6/2)
!standard 6.5(5.8/2)
!standard 6.5(8/2)
!class binding interpretation 07-11-11
!status Amendment 201Z 08-11-26
!status WG9 Approved 08-06-20
!status ARG Approved 7-1-1 06-11-11
!status work item 06-11-19
!status received 06-11-19
!priority Medium
!difficulty medium
!subject Extended return statements for class-wide functions
!summary
If a function returns a class-wide type, the object of an extended_return_statement can be declared with a specific type that is covered by the class-wide type.
!question
Imagine a device driver that registers a constructor function when it is loaded. The constructor function returns Device'Class. Device is a limited type.
Consider how you would use an extended return to write the SCSI device constructor:
return Obj : <specific type> do -- Not legal
This is not legal because we require the type here to be the same as that of the function [defined in 6.5(5.2/2)]. Instead, you have to write something like:
return Obj : Device'Class := Func do
where the result type of Func is the desired specific tagged type.
It seems that the first case really should be legal. An alternative view suggests that the example is inappropriate; a constructor should be a dispatching function, not something returning a classwide type. You could write a factory this way, but the actual constructs should probably still be dispatching routines. However, arguments from usage patterns are difficult to decide. Note that this is somewhat similar to the unconstrained case, where we allow giving constraints. For example:
function F return String is begin return Obj : String(1..10) do ...
!recommendation
(See summary.)
!wording
Modify RM 6.5(5.2/2) as follows:
If the result subtype of the function is defined by a subtype_mark, the return_subtype_indication shall be a subtype_indication. The type of the subtype_indication shall be {covered by} the result type of the function. If the result subtype of the function is constrained, then the subtype defined by the subtype_indication shall also be constrained and shall statically match this result subtype. If the result subtype of the function is unconstrained, then the subtype defined by the subtype_indication shall be a definite subtype, or there shall be an expression.
Append after 6.5(5.3/2) (as a new item in the same bulleted list):
* If the result subtype of the function is class-wide, the accessibility level of the type of the subtype defined by the return_subtype_indication shall not be statically deeper than that of the master that elaborated the function body.
Modify 6.5(5.6/2) as follows:
* If the result subtype of the function is class-wide, the accessibility level of the type of the expression {(if any)} of the return statement...
Add to the end of RM 6.5(5.8/2):
... {A check is made that the value of the return object belongs to the function result subtype. Constraint_Error is raised if this check fails.}
AARM Ramification: Other rules ensure that this check cannot fail unless the function has a class-wide result subtype where the associated specific subtype is constrained.
Modify RM 6.5(8/2) as follows:
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 type of the subtype_indication if it is specific, or otherwise 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.
!discussion
The example in the "question" section above illustrates how the existing rule can be annoying. Arguing from consistency with the existing ability to specify a more constrained subtype in the return_subtype_indication, it seems reasonable to allow an object of any type that would satisfy the "expected type" for the expression in a simple_return_statement. The expected type for that expression is the function result type. That means that if the function result type is class-wide, then the return expression in a simple_return_statement could be of any type covered by the class-wide type. It seems reasonable therefore to allow the declaration of an object of any such type as the return object in an extended_return_statement.
Note that the main wording changes are quite simple, namely adding "covered by" to paragraph 5.2/2, and specifying that the tag comes from the subtype_ indication if it is specifc.
We also need a rule to handle static rejection for accessibility failure of cases like
function Foo return T'Class is type Local_Extension is new T with null record; begin return X : Local_Extension; end Foo;
Associated with that is the addition of "(if any)" to 6.5(5.6/2), since an expression is no longer required in an extended return (since a specific type can be given).
The change in 5.8/2 is actually patching a hole that already existed, since one could have a result subtype of "S1'Class" but specify a nominal subtype for the return object of "S2'Class" where S1 and S2 are both subtypes of the same type, and nowhere were we checking that the return object satisfied the constraints of S1.
!example
-- Every device kind needs a constructor that creates a device control -- block for that kind of device. This constructor is registered -- in a table indexed by the device kind ID. type Device_Constructor is access function (P : Param) return Device'Class;
procedure Register_Device_Constructor(Kind : Device_Kind_ID;
Constructor : Device_Constructor);
....
-- Here we define such a constructor, and then register it -- Note that it is natural to make the return object of the -- specific device type, rather than the class-wide type -- used for the function result subtype. function Make_My_Device(P : Param) return Device'Class is begin
return Result : My_Device do
Init_My_Device(Result);
end return;
end Make_Device;
begin Register_Device_Constructor(My_Device_Kind, Make_My_Device'access);
!corrigendum 6.5(5.2/2)
Replace the paragraph:
by:
!corrigendum 6.5(5.3/2)
Insert after the paragraph:
the new paragraph:
!corrigendum 6.5(5.6/2)
Replace the paragraph:
by:
!corrigendum 6.5(5.8/2)
Replace the paragraph:
For the execution of an extended_return_statement, the subtype_indication or access_definition is elaborated. This creates the nominal subtype of the return object. If there is an expression, it is evaluated and converted to the nominal subtype (which might raise Constraint_Error — see 4.6); the return object is created and the converted value is assigned to the return object. Otherwise, the return object is created and initialized by default as for a stand-alone object of its nominal subtype (see 3.3.1). If the nominal subtype is indefinite, the return object is constrained by its initial value.
by:
For the execution of an extended_return_statement, the subtype_indication or access_definition is elaborated. This creates the nominal subtype of the return object. If there is an expression, it is evaluated and converted to the nominal subtype (which might raise Constraint_Error — see 4.6); the return object is created and the converted value is assigned to the return object. Otherwise, the return object is created and initialized by default as for a stand-alone object of its nominal subtype (see 3.3.1). If the nominal subtype is indefinite, the return object is constrained by its initial value. A check is made that the value of the return object belongs to the function result subtype. Constraint_Error is raised if this check fails.
!corrigendum 6.5(8/2)
Replace 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.
by:
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 type of the subtype_indication if it is specific, or otherwise 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.
!ACATS test
Create an ACATS test to check that the above changes work.
!appendix

From: Tucker Taft
Sent: Friday, May 18, 2007  8:37 AM

Here is the AI [This is version /01 of the AI - ED]
on allowing the type of the return object
in an extended_return_statement to be a specific type when
the result type is class-wide.  While writing this, I bumped
into what seems to be a longstanding hole in 4.6 (Type
Conversions).  Given two subtypes of T'Class, S1'Class
and S2'Class, we don't ever seem to check that when converting
from S2'Class to S1'Class, that the operand satisfies
the constraints of S1.  See the AI discussion section for
more on this.  A related hole seems to exist in 6.5
(Return Statements), where if the result subtype is
S1'Class, we allow the return object to be declared of
subtype S2'Class, and again never check against S1.
I tried to fix this latter hole, but ended up just
presuming that 4.6 did the right thing, which it doesn't.
So another AI is probably needed to address the 4.6 hole.

Ignoring this particular existing hole, the wording changes
were quite small.

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

From: Randy Brukardt
Sent: Friday, May 18, 2007  11:17 PM

I don't understand your hole. I don't think there is any hole in 6.5,
at least before your proposal. 6.5(5.2/2) requires static matching
of the return object subtype if the result subtype is constrained;
so if the result subtype S1'Class has a constraint, an object subtype
of S2'Class is illegal unless it has the same constraints. If the
result subtype doesn't have a constraint, there is nothing to check.

Humm, 4.6(51/2) says that after the conversion, there is a check that
the value satisfies any constraint of the target subtype if it is
constrained. What more do you need?

(If, for some bizarre reason, S1'Class is not considered constrained,
then it has no real constraints, because pretty much all of the rules
of the language are written "if constrained, then blah blah". It would
effectively act like was unconstrained. While I'd prefer this
interpretation, I don't think it was what was intended - I remember
*losing* that argument during Ada 95. And having constraints on
unconstrained subtypes seems like the first step on the road to madness...)

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

Summary of a private thread about AI05-0075-1:

The AI has:

A couple of other changes that really should have gone into AI05-0032
are also included to handle static rejection of cases like

  function Foo return T'Class is
    type Local_Extension is new T with null record;
  begin
    return X : Local_Extension;
  end Foo;

---

But this case is statically illegal, because "Local_Extension" does not
have the same type as "T'Class" -- and 6.5(5.2/2) requires that for extended
return statements (for functions with ordinary subtype returns -- there are
different rules for access results).

---

But Steve is talking about AI05-0032, which allows
the return_subtype_indication to determine any
type that is *covered* by the result type of the
function.

---

Why? That AI has never been approved. (And thus isn't in *my* AARM.) If it needs
additional rules in order to work, then those rules need to be in that AI,
not in some unrelated AI that happens to modify that paragraph. That's
especially true as AI-32 is an Amendment AI, and may not be implemented for
years if we don't vote to change the status.

---

Good point, Randy.  AI05-0032 should be amended
instead of adding it to this AI.

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

From: Randy Brukardt
Sent: Monday, December 3, 2007  11:59 PM

At the recent meeting, I objected to the proposed added wording for 6.5(23/2):

    In the case of a function, the function_call denotes a constant view of the
    return object. {If the result type is class-wide, this view is that created
    by the view conversion of the return object to the function result subtype
    (see above).}

I originally said that I thought this addition was redundant. Tucker gave his
reason for including it as "the return object has a specific type, and the caller
needs to see a different view". I was concerned that there was something wrong
with this, but we pushed on to a vote without much additional consideration. (I
have the feeling that my concerns weren't taken particularly seriously because
of my previous objection to the entire concept; while they were totally unrelated
to that, I can imagine how some people may have felt I was just trying to obstruct
completing the AI.)

Anyway, I'm still convinced that either this text is completely redundant, or
there is something seriously wrong with the language. (I think it is the former,
but I could be wrong.)

My basic point is that while objects may have a nominal class-wide subtype, they
never have a real type that is class-wide. That is, the actual object always has
some specific tagged type. We don't worry in the Standard where that class-wide
view comes from; it just appears when needed (like many other kinds of views).

So I don't think we need special wording to cover this case.

More specifically, an extended_return_statement has rather similar semantics to
an allocator (which, BTW, is one of the best arguments for this AI in general).
Consider that:

     function F ... return T'Class is
         ...
         return Obj : T do ...

is very similar to

     type Any_T is access all T'Class;
     Ptr : Any_T := new T;

But I can't find any wording in 4.8 that says that the object created by "new T"
is view converted to the designated type and that this is the view accessed by the
designated type. I presume that is because it isn't necessary; any access through
the access type will have the right view (a class-wide type), even though the
object itself still has a specific type.

And the same is true for a function call; any reference of the return object after
the return of the function will have a class-wide nominal subtype, and that is
fine and expected. Before the return (within the extended return statement),
the object will have the nominal subtype it was declared with, and that too is
expected.

The only issue I can think of is if the class-wide type is partially constrained,
and a constraint check is needed -- but that is separate from the class-wide view.
Moreover, the rule added to 6.5(5.8/2) takes care of the needed conversion. (I
wouldn't call it a conversion, just a check, but that's nit-picking.)

So I still don't see the point of this extra wording; it just seems to confuse
the issue by seeming to say that something else is going on than the normal case.
And this is just the normal case for class-wide objects (as noted above for
allocators, and also is the normal case for class-wide object declarations).
Why is this case different??

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

From: Randy Brukardt
Sent: Tuesday, December 4, 2007  12:29 AM

The discussion of AI-32 seems to imply that the only reason for 6.5(5.8/2)
and 6.5(23/2) is to make this check. (There also was a lot of irrelevant
junk that pertains to AI-57 rather than AI-32, which I deleted.) In that
case, I don't know why we aren't just using similar wording to allocators -
4.8(10/2) says: "A check is made that the value of the object belongs to the
designated subtype."  Seems to me that 4.8(5.8/2) should just say:
   "If the result type is class-wide, a check is made that the value of the
object belongs to the function result subtype."
...and then no change to 4.8(23/2) is needed.

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

From: Tucker Taft
Sent: Tuesday, December 4, 2007  6:05 AM

I presume you meant 6.5(5.8/2), not 4.8(5.8/2).
And I agree, adding explicit wording, as you suggest here,
about a check would seem to address the primary concern.
Your suggested addition could say simply "A check is made..."
and not bother with the "class-wide" prerequisite, since
the check is certain to succeed in other cases.

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

From: Randy Brukardt
Sent: Tuesday, December 4, 2007  2:21 PM

OK. I'll use this wording in the final AI (as an "editorial review" change)
unless someone objects. (If someone objects, we'll have to discuss it at a
meeting again.)

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

From: Dan Eilers
Sent: Wedenesday, November 25, 2009  10:46 AM

The !Summary of AI05-0032 (Extended return statements for class-wide
functions) refers to a "specific type", but should probably say
"specific type or classwide type", to be consistent with the !Wording.

 "If a function returns a class-wide type, the object of an
  extended_return_statement can be declared with a specific type
  {or class-wide type} that is covered by the class-wide type."

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

Questions? Ask the ACAA Technical Agent