comp.lang.ada
 help / color / mirror / Atom feed
* Question about controlled types.
@ 2008-02-07  1:19 Peter C. Chapin
  2008-02-07  4:54 ` Hyman Rosen
  2008-02-07 16:47 ` Adam Beneschan
  0 siblings, 2 replies; 7+ messages in thread
From: Peter C. Chapin @ 2008-02-07  1:19 UTC (permalink / raw)


I'm trying to understand the subtleties of controlled types. In the RM, 
section 7.6, paragraph 17 (I'm looking at the Ada 2005 version) I see:

"For an assignment_statement, after the name and expression have been 
evaluated, and any conversion (including constraint checking) has been 
done, an anonymous object is created, and the value is assigned into it; 
that is, the assignment operation is applied. (Assignment includes value 
adjustment.) The target of the assignment_statement is then finalized. 
The value of the anonymous object is then assigned into the target of 
the assignment_statement. Finally, the anonymous object is finalized."

So suppose we have

type Ptr is access Whatever;
type Thing is new Ada.Finalization.Controlled with
   record
     P : Ptr;
   end record;

Thus Things hold a handle to an object allocated on the heap. Now 
suppose A and B are Things. Then consider

A := B;

If I understand the paragraph above, an anonymous object is created and 
the values of B's components are copied into that object. Adjust is then 
used on the anonymous object. Let's imagine that Adjust makes an 
independent copy of the object accessed via the P component. If an 
exception occurs during this adjustment (Storage_Error, say) the value 
of A is not affected. This is good.

Next A is finalized and then the anonymous object is then assigned to A. 
Here is my question: is Adjust used again to adjust the value of A? It 
would seem so because the anonymous object is finalized after this and 
we wouldn't want that finalization to disrupt A's eventual value (assume 
Finalize deallocates the object accessed by the P component). However, 
if that is all true then why use Adjust on the anonymous object?

It seems to me that a more appropriate sequence of events would be

1. Create the anonymous object as above (using Adjust, etc).
2. Finalize A as above.
3. Copy the components of the anonymous object to A without using Adjust.
4. Don't Finalize the anonymous object.

My concern here is exception safety. I'd like it to be the case that 
when I do

A := B;

if there is insufficient memory to build a proper copy of B's complete 
representation (including heap structures), I'd like A to be left 
unchanged. Yet the semantics described in the RM seem to say that A will 
be finalized (thus destroying its value) before the copy of B that 
eventually gets stored in A is made. Thus if that copy operation fails 
with Storage_Error, A is left corrupted.

This question seems very relevant to the Ada 2005 container library. For 
example if A and B are vectors, and if B is a very large vector that I'm 
trying to copy, I'd really like for A to be left unchanged should 
Storage_Error occur during that copy.

Peter



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Question about controlled types.
  2008-02-07  1:19 Question about controlled types Peter C. Chapin
@ 2008-02-07  4:54 ` Hyman Rosen
  2008-02-07 16:30   ` Robert A Duff
  2008-02-08  3:47   ` Peter C. Chapin
  2008-02-07 16:47 ` Adam Beneschan
  1 sibling, 2 replies; 7+ messages in thread
From: Hyman Rosen @ 2008-02-07  4:54 UTC (permalink / raw)


Peter C. Chapin wrote:
> 3. Copy the components of the anonymous object to A without using Adjust.

This is ill-advised. Objects may hold pointers to their own
submembers. If you assign without adjusting, those internal
pointers will point to the anonymous object, not to A.

There are reasons for doing assignment like this in Ada (or
at least I think so - I don't really know Ada), but the C++
assignment operator model is much cleaner.



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Question about controlled types.
  2008-02-07  4:54 ` Hyman Rosen
@ 2008-02-07 16:30   ` Robert A Duff
  2008-02-08  3:31     ` Peter C. Chapin
  2008-02-08  3:47   ` Peter C. Chapin
  1 sibling, 1 reply; 7+ messages in thread
From: Robert A Duff @ 2008-02-07 16:30 UTC (permalink / raw)


Hyman Rosen <hyrosen@mail.com> writes:

> There are reasons for doing assignment like this in Ada (or
> at least I think so - I don't really know Ada), but the C++
> assignment operator model is much cleaner.

Right, the C++ way has the advantage that a single operation can get its
hands on the left- and right-hand sides at the same time.  The reason
Ada doesn't have this feature is that we couldn't figure out how to make
it work for records whose discriminants can change.  And we couldn't
simply outlaw that case, because it would introduce a violation of the
generic contract model.

- Bob



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Question about controlled types.
  2008-02-07  1:19 Question about controlled types Peter C. Chapin
  2008-02-07  4:54 ` Hyman Rosen
@ 2008-02-07 16:47 ` Adam Beneschan
  2008-02-08  3:46   ` Peter C. Chapin
  1 sibling, 1 reply; 7+ messages in thread
From: Adam Beneschan @ 2008-02-07 16:47 UTC (permalink / raw)


On Feb 6, 5:19 pm, "Peter C. Chapin" <pcha...@sover.net> wrote:
> I'm trying to understand the subtleties of controlled types. In the RM,
> section 7.6, paragraph 17 (I'm looking at the Ada 2005 version) I see:
>
> "For an assignment_statement, after the name and expression have been
> evaluated, and any conversion (including constraint checking) has been
> done, an anonymous object is created, and the value is assigned into it;
> that is, the assignment operation is applied. (Assignment includes value
> adjustment.) The target of the assignment_statement is then finalized.
> The value of the anonymous object is then assigned into the target of
> the assignment_statement. Finally, the anonymous object is finalized."

Did you read the rest of the paragraph?  "As explained below, the
implementation may eliminate the intermediate anonymous object, so
this description subsumes the one given in 5.2, 'Assignment
Statements'".  If you read the "below" part to which this sentence
refers, in the Implementation Permissions, you'll find that in the
case where an object is assigned to another object (i.e. not a
function call or an aggregate), you really need the intermediate
anonymous object only if there's the possibility of an overlap.
Suppose A is an array of controlled objects, and you say

  A (2 .. 10) := A (1 .. 9);

Now a compiler might well decide to create a temporary object to hold
the array slice being assigned, because it will have to finalize
elements A(2) through A(9) before assigning them, and it will have to
store those values somewhere before doing that finalization.  (Even in
that case, the temporary object isn't necessary since the compiler
could generate code to copy one element at a time.)



> So suppose we have
>
> type Ptr is access Whatever;
> type Thing is new Ada.Finalization.Controlled with
>    record
>      P : Ptr;
>    end record;
>
> Thus Things hold a handle to an object allocated on the heap. Now
> suppose A and B are Things. Then consider
>
> A := B;
>
> If I understand the paragraph above, an anonymous object is created and
> the values of B's components are copied into that object.

Based on the Implementation Permission, I will bet that *no* compiler
will actually create an anonymous temporary object.  A and B can't
overlap unless they're the same object (possible if one of them is a
subprogram parameter), and in that case the compiler could simply
generate code to check the addresses and see if they're the same
object.

> Adjust is then
> used on the anonymous object. Let's imagine that Adjust makes an
> independent copy of the object accessed via the P component. If an
> exception occurs during this adjustment (Storage_Error, say) the value
> of A is not affected. This is good.
>
> Next A is finalized and then the anonymous object is then assigned to A.
> Here is my question: is Adjust used again to adjust the value of A? It
> would seem so because the anonymous object is finalized after this and
> we wouldn't want that finalization to disrupt A's eventual value (assume
> Finalize deallocates the object accessed by the P component). However,
> if that is all true then why use Adjust on the anonymous object?
>
> It seems to me that a more appropriate sequence of events would be
>
> 1. Create the anonymous object as above (using Adjust, etc).
> 2. Finalize A as above.
> 3. Copy the components of the anonymous object to A without using Adjust.
> 4. Don't Finalize the anonymous object.

The trouble is, you're thinking about one particular use of controlled
types, where Adjust's sole purpose is to make a copy of the data
pointed to by a pointer (or multiple pointers).  That's a common use,
but not the only possible use.  Although I can't immediately think of
an example where your sequence would screw things up, the AARM refers
to subcomponents that are "self-referential or otherwise position-
dependent" (I think Hyman was referring to this same notion here).


> My concern here is exception safety. I'd like it to be the case that
> when I do
>
> A := B;
>
> if there is insufficient memory to build a proper copy of B's complete
> representation (including heap structures), I'd like A to be left
> unchanged. Yet the semantics described in the RM seem to say that A will
> be finalized (thus destroying its value) before the copy of B that
> eventually gets stored in A is made. Thus if that copy operation fails
> with Storage_Error, A is left corrupted.
>
> This question seems very relevant to the Ada 2005 container library. For
> example if A and B are vectors, and if B is a very large vector that I'm
> trying to copy, I'd really like for A to be left unchanged should
> Storage_Error occur during that copy.

Like I said above, A := B probably isn't going to involve an anonymous
object at all, because of the Implementation Permissions in this
section.  This means that A will be finalized before any attempt is
made to copy the structure pointed to by B; if your idea is that A
must be unchanged if the copy of B could raise a Storage_Error, then
you'll probably have to roll your own:

    procedure Safe_Assign (A : in out Thing; B : in Thing) is
       Temp : Thing;
    begin
       Temp := B;
       A.Ptr := Temp.Ptr;
       Temp.Ptr := null;
    end Safe_Assign;

I'll admit that that probably won't help with the container library,
though.

                               -- Adam



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Question about controlled types.
  2008-02-07 16:30   ` Robert A Duff
@ 2008-02-08  3:31     ` Peter C. Chapin
  0 siblings, 0 replies; 7+ messages in thread
From: Peter C. Chapin @ 2008-02-08  3:31 UTC (permalink / raw)


Robert A Duff wrote:

> Right, the C++ way has the advantage that a single operation can get its
> hands on the left- and right-hand sides at the same time.  The reason
> Ada doesn't have this feature is that we couldn't figure out how to make
> it work for records whose discriminants can change.  And we couldn't
> simply outlaw that case, because it would introduce a violation of the
> generic contract model.

That's interesting thanks.

Peter



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Question about controlled types.
  2008-02-07 16:47 ` Adam Beneschan
@ 2008-02-08  3:46   ` Peter C. Chapin
  0 siblings, 0 replies; 7+ messages in thread
From: Peter C. Chapin @ 2008-02-08  3:46 UTC (permalink / raw)


Adam Beneschan wrote:

> Did you read the rest of the paragraph?  "As explained below, the
> implementation may eliminate the intermediate anonymous object, so
> this description subsumes the one given in 5.2, 'Assignment
> Statements'".

Well the anonymous object, and its existence or lack thereof, is not 
what I'm worried about... I admit my earlier post may have been unclear 
on that point. My main concern was in how to use controlled types in an 
"exception safe" way.

However, it occurred to me today that if the implementation supports 
garbage collection my example would work fine. In that case Finalize 
wouldn't have to do anything. Adjust would still make a copy, but if 
creating that copied failed during the assignment to A (the final 
target), it would still be okay to share heap structures with the 
anonymous object. When the anonymous object vanished those structures 
would just be owned by A and not become garbage as they would have 
otherwise.

Anyway, thanks for your detailed reply!

Peter



^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Question about controlled types.
  2008-02-07  4:54 ` Hyman Rosen
  2008-02-07 16:30   ` Robert A Duff
@ 2008-02-08  3:47   ` Peter C. Chapin
  1 sibling, 0 replies; 7+ messages in thread
From: Peter C. Chapin @ 2008-02-08  3:47 UTC (permalink / raw)


Hyman Rosen wrote:

> This is ill-advised. Objects may hold pointers to their own
> submembers. If you assign without adjusting, those internal
> pointers will point to the anonymous object, not to A.

Hmmm. I see what you are saying.

Peter



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2008-02-08  3:47 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-02-07  1:19 Question about controlled types Peter C. Chapin
2008-02-07  4:54 ` Hyman Rosen
2008-02-07 16:30   ` Robert A Duff
2008-02-08  3:31     ` Peter C. Chapin
2008-02-08  3:47   ` Peter C. Chapin
2008-02-07 16:47 ` Adam Beneschan
2008-02-08  3:46   ` Peter C. Chapin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox