Version 1.1 of acs/ac-00144.txt

Unformatted version of acs/ac-00144.txt version 1.1
Other versions for file acs/ac-00144.txt

!standard 7.5(8.1/2)          07-08-03 AC95-00144/01
!class confirmation 07-08-03
!status received no action 07-08-03
!status received 07-06-26
!subject Build-in-place and extended return
!summary
!appendix

!topic Build-in-place and extended return
!reference 7.5(8.1)
!from Adam Beneschan 07-06-26
!discussion


Just for clarification:

7.5(8.1) says that when a function call of a limited type is used to
initialize an object, the function call must be constructed directly
in the new object.

Does this mean that, in this case:

   X : Limited_Type := Func(1);

if Func's return is by an extended return:

   return Ret_Object : Limited_Type do
     ... <sequence-of-statements>
   end return;

that while the sequence of statements is executing, Ret_Object is
essentially a view of X (or overlaps X, if you don't want to use the
term "view"), so that anything done to Ret_Object gets done to X?  If
not, what does it mean?

I did think of a case where a precise definition of the semantics
could make a difference.

procedure Test1 is
   type Limited_Type is limited record
       F1 : Integer;  -- no default expressions
       F2 : Integer;
   end record;

   function Func (Z : Integer) return Limited_Type is
   begin
      return Ret : Limited_Type do
         Ret.F1 := 10;
         if Z > 0 then
            goto LABEL1;
         end if;
         Ret.F2 := 20;
      end return;
   <<LABEL1>>
      return Ret2 : Limited_Type do
         Ret2.F2 := 30;
      end return;
   end Func;

   X : Limited_Type := Func(1);
begin
   Text_IO.Put_Line (Integer'Image (X.F1));
end Test1;

If build-in-place weren't required (e.g. if Limited_Type weren't
limited), then X.F1 could be anything, since its value is the value of
Ret2, and Ret2.F1 isn't initialized.

But in the above case, the type is limited, and the function call must
be constructed directly in X according to the rules.  Now, is it true
that any correct Ada compiler must generate code that outputs 10?

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

From: Robert A. Duff
Sent: Saturday, June 30, 2007  9:16 AM

...
> Does this mean that, in this case:
>
>    X : Limited_Type := Func(1);
>
> if Func's return is by an extended return:
>
>    return Ret_Object : Limited_Type do
>      ... <sequence-of-statements>
>    end return;
>
> that while the sequence of statements is executing, Ret_Object is
> essentially a view of X (or overlaps X, if you don't want to use the
> term "view"), so that anything done to Ret_Object gets done to X?  If
> not, what does it mean?

It means that X and Ret_Object are the same object.
Probably the best way to think about it is that
X does not actually exist until the moment program
reaches "end return" -- at that point, Ret_Object
becomes X.

> I did think of a case where a precise definition of the semantics
> could make a difference.
>
> procedure Test1 is
>    type Limited_Type is limited record
>        F1 : Integer;  -- no default expressions
>        F2 : Integer;
>    end record;
>
>    function Func (Z : Integer) return Limited_Type is
>    begin
>       return Ret : Limited_Type do
>          Ret.F1 := 10;
>          if Z > 0 then
>             goto LABEL1;
>          end if;
>          Ret.F2 := 20;
>       end return;
>    <<LABEL1>>
>       return Ret2 : Limited_Type do
>          Ret2.F2 := 30;
>       end return;
>    end Func;
>
>    X : Limited_Type := Func(1);
> begin
>    Text_IO.Put_Line (Integer'Image (X.F1));
> end Test1;
>
> If build-in-place weren't required (e.g. if Limited_Type weren't
> limited), then X.F1 could be anything, since its value is the value of
> Ret2, and Ret2.F1 isn't initialized.

Right.

> But in the above case, the type is limited, and the function call must
> be constructed directly in X according to the rules.  Now, is it true
> that any correct Ada compiler must generate code that outputs 10?

No.  I hope not.  The implementation is allowed to allocate memory for Ret,
then zero it out and throw it away at the "goto", then allocate memory for Ret2
at a different address.  Ret2 and X must end up being the same thing.  Please
don't ask me to define "same thing" precisely -- I'm a programming language
designer, not a philosopher.  ;-)

It does NOT mean that X'Address = Ret2'Address, because a garbage-collecting
implementation could move X to a different address at any time.  However you
could put a self-referential access value in Limited_Type
(see http://www.adacore.com/2007/05/21/gem-2/ for an example type), and then
X'Self = X'Access must be true.

I believe the above example will output 10 in GNAT (I didn't try it, but I did
some parts of the implementation.)  However, note that the size of the result
is not always known until we get to the return:

   type Limited_Type (Length : Natural) is limited
      record
         Chars : String (1..Length);
      end record;

   function Func (Z : Integer) return Limited_Type is
   begin
      return Ret : Limited_Type (Length => 1) do
         Ret.Chars (1) := 'x';
         raise Cain;
      end return;
   exception
      when Cain =>
         return Ret2 : Limited_Type (Length => 2) do
            -- Ret2.Chars (1) is probably not 'x' here.
            Ret2.Chars (2) := 'y';
            raise Cain;
         end return;
   end Func;

   type Acc is access all Limited_Type;
   for Acc'Storage_Pool use My_Pool;

   X.all := new Limited_Type'(Func(123));
   -- X.Chars (1) is probably not 'x' here.

The implementation will allocate Ret, then free it, then allocate Ret2.
We can arrange for My_Pool to ensure that they are allocated at
different addresses.  We can also arrange to trash the memory
occupied by Ret when it is freed.

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

From: Adam Beneschan
Sent: Monday, July 2, 2007  12:35 PM

> It means that X and Ret_Object are the same object.
> Probably the best way to think about it is that
> X does not actually exist until the moment program
> reaches "end return" -- at that point, Ret_Object
> becomes X.

That seems sensible.  I guess part of my problem has to do with the
term "build-in-place"; the term seems to imply that there's already a
"place" waiting for the program to build something in, which Bob's
explanation would seem to say is *not* the case.  The RM doesn't use
that term (it's only in the AARM); but 7.6(21) does say this, an in
Implementation Permission: "For an aggregate or function call whose
value is assigned into a target object, the implementation need not
create a separate anonymous object if it can safely create the value
of the aggregate or function call directly in the target object".  (Of
course, that sentence was written long before extended returns
existed.)  To someone like me who tends to take things literally, the
last part of the above-quoted sentence, the way it's worded, seems to
imply that that there's already a preexisting target object for the
function result to be created in (and the meaning of this language is
not further defined in the RM).  I also realize that this is an
Implementation Permission, and the original example deals with an
Implementation Requirement, but I've always thought of the
Implementation Permission as permitting the implementation to do the
exact same thing that is required for certain types.

In any case, someone who looks at the "create a value directly in the
target object" language, or the "build-in-place" terminology, might
think that the language would indeed require X.F1 to be 10 in this
example:

> > I did think of a case where a precise definition of the semantics
> > could make a difference.
> >
> > procedure Test1 is
> >    type Limited_Type is limited record
> >        F1 : Integer;  -- no default expressions
> >        F2 : Integer;
> >    end record;
> >
> >    function Func (Z : Integer) return Limited_Type is
> >    begin
> >       return Ret : Limited_Type do
> >          Ret.F1 := 10;
> >          if Z > 0 then
> >             goto LABEL1;
> >          end if;
> >          Ret.F2 := 20;
> >       end return;
> >    <<LABEL1>>
> >       return Ret2 : Limited_Type do
> >          Ret2.F2 := 30;
> >       end return;
> >    end Func;
> >
> >    X : Limited_Type := Func(1);
> > begin
> >    Text_IO.Put_Line (Integer'Image (X.F1));
> > end Test1;

since if the function result is supposed to be "created directly in
the target object", then setting Ret.F1 should also set X.F1.  But I'd
agree with Bob that this should not be required behavior (although
many implementations would output 10).

But perhaps it's not that big a deal---it's likely that this would
never be an issue in the real world.

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

From: Pascal Leroy (private mail of July 30, 2007)

I am actually opposed to tightening the definition here.  In the
example given in the message, I think it would be fine for a compiler to
actually *not* build in place and later do a copy, as long as there is no
way that the user could tell (no tasks, no controlled types, etc.).
Obviously we hope that implementations won't do that, but it's not
testable without looking at the generated code.

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


Questions? Ask the ACAA Technical Agent