comp.lang.ada
 help / color / mirror / Atom feed
* Why is the destructor called multiple times after I declare an object?
@ 2016-01-11  1:37 Andrew Shvets
  2016-01-11  2:18 ` Jeffrey R. Carter
  0 siblings, 1 reply; 27+ messages in thread
From: Andrew Shvets @ 2016-01-11  1:37 UTC (permalink / raw)


Hello,

This is the code in question:
http://pastebin.com/zf7yEvpe

When I run the executable, this is what I get:
$ ./main_animal 
Resetting values of Creat to defaults.
Resetting values of Creat to defaults.
Resetting values of Creat to defaults.
Resetting values of Creat to defaults.
 The animal:
  The name:        dog
  Number of legs:   4
  Weight in grams:  3000
  Height in cm:     40
 The animal:
  The name:        Elephant
  Number of legs:   4
  Weight in grams:  4000000
  Height in cm:     500
Resetting values of Creat to defaults.
Resetting values of Creat to defaults.

Why is "Resetting values of Creat to defaults." displaying 6 times as opposed to just twice?


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11  1:37 Why is the destructor called multiple times after I declare an object? Andrew Shvets
@ 2016-01-11  2:18 ` Jeffrey R. Carter
  2016-01-11  3:35   ` Andrew Shvets
  2016-01-11 16:29   ` Brian Drummond
  0 siblings, 2 replies; 27+ messages in thread
From: Jeffrey R. Carter @ 2016-01-11  2:18 UTC (permalink / raw)


On 01/10/2016 06:37 PM, Andrew Shvets wrote:
> 
> Why is "Resetting values of Creat to defaults." displaying 6 times as opposed to just twice?

ARM 7.6(13-17.1) apply here. Note that Init declares a local variable of the
type, which is finalized when Init returns, and that the anonymous object that
is the result of the call to Init is finalized after initialization is finished.
This gives you 2 calls to Finalize for each variable of the type declared and
initialized by a call to Init. Finally you have a call to Finalize for each
variable of the type when the program ends.

http://www.adaic.org/resources/add_content/standards/12rm/html/RM-7-6.html

-- 
Jeff Carter
"Death awaits you all, with nasty, big, pointy teeth!"
Monty Python & the Holy Grail
20

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11  2:18 ` Jeffrey R. Carter
@ 2016-01-11  3:35   ` Andrew Shvets
  2016-01-11 17:02     ` Brian Drummond
  2016-01-11 16:29   ` Brian Drummond
  1 sibling, 1 reply; 27+ messages in thread
From: Andrew Shvets @ 2016-01-11  3:35 UTC (permalink / raw)


On Sunday, January 10, 2016 at 9:18:57 PM UTC-5, Jeffrey R. Carter wrote:
> On 01/10/2016 06:37 PM, Andrew Shvets wrote:
> > 
> > Why is "Resetting values of Creat to defaults." displaying 6 times as opposed to just twice?
> 
> ARM 7.6(13-17.1) apply here. Note that Init declares a local variable of the
> type, which is finalized when Init returns, and that the anonymous object that
> is the result of the call to Init is finalized after initialization is finished.
> This gives you 2 calls to Finalize for each variable of the type declared and
> initialized by a call to Init. Finally you have a call to Finalize for each
> variable of the type when the program ends.
> 
> http://www.adaic.org/resources/add_content/standards/12rm/html/RM-7-6.html
> 
> -- 
> Jeff Carter
> "Death awaits you all, with nasty, big, pointy teeth!"
> Monty Python & the Holy Grail
> 20

I see.  Thank you for your explanation.


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11  2:18 ` Jeffrey R. Carter
  2016-01-11  3:35   ` Andrew Shvets
@ 2016-01-11 16:29   ` Brian Drummond
  2016-01-11 17:20     ` Simon Wright
                       ` (2 more replies)
  1 sibling, 3 replies; 27+ messages in thread
From: Brian Drummond @ 2016-01-11 16:29 UTC (permalink / raw)


On Sun, 10 Jan 2016 19:18:53 -0700, Jeffrey R. Carter wrote:

> On 01/10/2016 06:37 PM, Andrew Shvets wrote:
>> 
>> Why is "Resetting values of Creat to defaults." displaying 6 times as
>> opposed to just twice?
> 
> ARM 7.6(13-17.1) apply here. Note that Init declares a local variable of
> the type, which is finalized when Init returns, and that the anonymous
> object that is the result of the call to Init is finalized after
> initialization is finished.
> This gives you 2 calls to Finalize for each variable of the type
> declared and initialized by a call to Init. Finally you have a call to
> Finalize for each variable of the type when the program ends.
> 
> http://www.adaic.org/resources/add_content/standards/12rm/html/
RM-7-6.html


Interesting. I thought you could eliminate one of those local copies 
using "extended return" in the Init function, as in:

  function Init return Creature is
  begin
    return TempCreature : Creature do 
       TempCreature.Name          :=  
Ada.Strings.Unbounded.To_Unbounded_String("dog");
       TempCreature.Legs          := 4;
       TempCreature.WeightInGrams := 3000;
       TempCreature.HeightInCm    := 40;
     end return;
  end Init;

Somehow I expected "extended return" to allocate space and "build in 
place" during the execution of the return statement. 

Is this something legal that Gnat doesn't take advantage of? Or is there 
a Gnat option I'm not aware of?

Of course instead of a default constructor, the record can be populated 
with default values, to eliminate both the "Init" call and two temporary 
objects, but the "problem" remains for the other constructor.

-- Brian


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11  3:35   ` Andrew Shvets
@ 2016-01-11 17:02     ` Brian Drummond
  0 siblings, 0 replies; 27+ messages in thread
From: Brian Drummond @ 2016-01-11 17:02 UTC (permalink / raw)


On Sun, 10 Jan 2016 19:35:43 -0800, Andrew Shvets wrote:

> On Sunday, January 10, 2016 at 9:18:57 PM UTC-5, Jeffrey R. Carter
> wrote:
>> On 01/10/2016 06:37 PM, Andrew Shvets wrote:
>> > 
>> > Why is "Resetting values of Creat to defaults." displaying 6 times as
>> > opposed to just twice?
>> 
>> ARM 7.6(13-17.1) apply here. Note that Init declares a local variable
>> of the type, 
> 
> I see.  Thank you for your explanation.

Note that for the default initialisation case, if you override 
Initialize, it will be called automatically.

  overriding procedure Initialize(
    Creat : in out Creature) is
  begin
    Ada.Text_IO.Put_Line("Initializing values of Creat to defaults.");

    Creat.Name          := Ada.Strings.Unbounded.To_Unbounded_String
("Ferret");
    Creat.Legs          := 3;
    Creat.WeightInGrams := 100;
    Creat.HeightInCm    := 7;
  end Initialize;

(yes the ferret lost a leg somehow).

Because it's a procedure, with In Out parameters, it operates in-place, 
avoiding both the creation of a temporary object, and assignment.

Now the main program
  Var1 : Animal.Creature; --:= Animal.Init; -- default constructor
  Var2 : Animal.Creature := Animal.Init("Elephant", 4, 4000000, 500);
begin
  Animal.Print_Record(Var1);
  Animal.Print_Record(Var2);
end main_animal;
initialises both variables to the defaults, then operates as Jeffery 
answered for the non-default Init function (I overrode Adjust to track 
assignments).

Initializing values of Creat to defaults.
Initializing values of Creat to defaults.
Adjusting Creature named Elephant
Resetting values of Creat to defaults.
Adjusting Creature named Elephant
Resetting values of Creat to defaults.
 The animal:
  The name:        Ferret
  Number of legs:   3
  Weight in grams:  100
  Height in cm:     7
 The animal:
  The name:        Elephant
  Number of legs:   4
  Weight in grams:  4000000
  Height in cm:     500
Resetting values of Creat to defaults.
Resetting values of Creat to defaults.

Rewriting Init (the non-default constructor) as a procedure would 
eliminate the intermediate variables and assignments. 

It would have to be called at the start of the main program instead of as 
an initialiser. Regrettably there seems to be no syntax for calling non-
default initializers on a controlled type.

-- Brian



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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11 16:29   ` Brian Drummond
@ 2016-01-11 17:20     ` Simon Wright
  2016-01-11 18:17     ` Bob Duff
  2016-01-12  0:43     ` Jeffrey R. Carter
  2 siblings, 0 replies; 27+ messages in thread
From: Simon Wright @ 2016-01-11 17:20 UTC (permalink / raw)


Brian Drummond <brian@shapes.demon.co.uk> writes:

> Somehow I expected "extended return" to allocate space and "build in
> place" during the execution of the return statement.
>
> Is this something legal that Gnat doesn't take advantage of? Or is
> there a Gnat option I'm not aware of?

ARM 7.6 (17.2-17.4) ff [1]: must build in place if the full type is
(immutably) limited, or (contains components that are)
controlled. Otherwise, up to the compiler. Doesn't say anything about
extended return.

[1] http://www.adaic.org/resources/add_content/standards/12rm/html/RM-7-6.html#p17.2

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11 16:29   ` Brian Drummond
  2016-01-11 17:20     ` Simon Wright
@ 2016-01-11 18:17     ` Bob Duff
  2016-01-11 21:10       ` Dmitry A. Kazakov
                         ` (2 more replies)
  2016-01-12  0:43     ` Jeffrey R. Carter
  2 siblings, 3 replies; 27+ messages in thread
From: Bob Duff @ 2016-01-11 18:17 UTC (permalink / raw)


Brian Drummond <brian@shapes.demon.co.uk> writes:

> Somehow I expected "extended return" to allocate space and "build in 
> place" during the execution of the return statement. 

Build-in-place is done for return of immutably-limited types,
whether or not the extended return syntax is used.

For nonlimited types, the compiler is free to use build-in-place if it
can prove that it's equivalent to return-by-copy.  It's often not
equivalent.  For example:

    X := Init;

What if Init raises an exception after partly modifying the result?
In that case X should not be modified, so the result object of Init
and X had better not be at the same address.

- Bob

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11 18:17     ` Bob Duff
@ 2016-01-11 21:10       ` Dmitry A. Kazakov
  2016-01-11 23:44         ` Randy Brukardt
  2016-01-12 12:41       ` Brian Drummond
  2016-01-13 20:18       ` Jacob Sparre Andersen
  2 siblings, 1 reply; 27+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-11 21:10 UTC (permalink / raw)


On 2016-01-11 19:17, Bob Duff wrote:
> Brian Drummond <brian@shapes.demon.co.uk> writes:
>
>> Somehow I expected "extended return" to allocate space and "build in
>> place" during the execution of the return statement.
>
> Build-in-place is done for return of immutably-limited types,
> whether or not the extended return syntax is used.

But you can leave one return statement on an exception, catch the 
exception, and then return through another return statement, with other 
discriminants and even other type of the result (if it is class-wide).

Therefore the result can be potentially allocated and reallocated any 
number of times. In which sense is this behavior 'in-place'?

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11 21:10       ` Dmitry A. Kazakov
@ 2016-01-11 23:44         ` Randy Brukardt
  2016-01-12  9:33           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 27+ messages in thread
From: Randy Brukardt @ 2016-01-11 23:44 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
news:n715oq$fae$1@gioia.aioe.org...
> On 2016-01-11 19:17, Bob Duff wrote:
>> Brian Drummond <brian@shapes.demon.co.uk> writes:
>>
>>> Somehow I expected "extended return" to allocate space and "build in
>>> place" during the execution of the return statement.
>>
>> Build-in-place is done for return of immutably-limited types,
>> whether or not the extended return syntax is used.
>
> But you can leave one return statement on an exception, catch the 
> exception, and then return through another return statement, with other 
> discriminants and even other type of the result (if it is class-wide).
>
> Therefore the result can be potentially allocated and reallocated any 
> number of times. In which sense is this behavior 'in-place'?

"Build-in-place" means simply that there is no temporary object, the object 
is created directly in its final resting place (that is the memory where it 
will live during its existence).

Whatever amount of effort it takes to figure out that final resting place is 
not part of the equation.

For many situations (and implementation models), the size of the result 
object will not change regardless of discriminants chosen, so the sort of 
thing you describe above cannot happen. For assignments to most existing 
objects, the discriminants/tag cannot be changed, and 6.5(24/3) allows the 
Constraint_Error exception to be raised early in scenarios like the one you 
lay out. (Such an exception cannot be handled within the function, as it 
happens at the call-site.) If the discriminants are mutable, the compiler 
already has to have some mechanism for changing them [either by using 
allocate-to-the-max or reallocation], and build-in-place uses that same 
mechanism, without the temporary. You'd get the same behavior when assigning 
some other object as with a build-in-place function.)

                                                 Randy.




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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11 16:29   ` Brian Drummond
  2016-01-11 17:20     ` Simon Wright
  2016-01-11 18:17     ` Bob Duff
@ 2016-01-12  0:43     ` Jeffrey R. Carter
  2 siblings, 0 replies; 27+ messages in thread
From: Jeffrey R. Carter @ 2016-01-12  0:43 UTC (permalink / raw)


On 01/11/2016 09:29 AM, Brian Drummond wrote:
> 
> Somehow I expected "extended return" to allocate space and "build in 
> place" during the execution of the return statement. 

Extended return is required for a limited type other than returning a function
call or aggregate. Limited types are build in place. But using an extended
return for a non-build-in-place type makes no difference.

> Is this something legal that Gnat doesn't take advantage of? Or is there 
> a Gnat option I'm not aware of?

A compiler can eliminate the anonymous object if it can be sure the result is
always the same as using it. It can build in place when not so required if it
can be sure the result is always the same as not building in place.

-- 
Jeff Carter
"C's solution to this [variable-sized array parameters] has real
problems, and people who are complaining about safety definitely
have a point."
Dennis Ritchie
25


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11 23:44         ` Randy Brukardt
@ 2016-01-12  9:33           ` Dmitry A. Kazakov
  2016-01-12 20:21             ` Randy Brukardt
  0 siblings, 1 reply; 27+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-12  9:33 UTC (permalink / raw)


On 12/01/2016 00:44, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:n715oq$fae$1@gioia.aioe.org...
>> On 2016-01-11 19:17, Bob Duff wrote:
>>> Brian Drummond <brian@shapes.demon.co.uk> writes:
>>>
>>>> Somehow I expected "extended return" to allocate space and "build in
>>>> place" during the execution of the return statement.
>>>
>>> Build-in-place is done for return of immutably-limited types,
>>> whether or not the extended return syntax is used.
>>
>> But you can leave one return statement on an exception, catch the
>> exception, and then return through another return statement, with other
>> discriminants and even other type of the result (if it is class-wide).
>>
>> Therefore the result can be potentially allocated and reallocated any
>> number of times. In which sense is this behavior 'in-place'?
>
> "Build-in-place" means simply that there is no temporary object, the object
> is created directly in its final resting place (that is the memory where it
> will live during its existence).
>
> Whatever amount of effort it takes to figure out that final resting place is
> not part of the equation.

Yes, but you said 'no temporary object' which is untrue because a return 
statement creates exactly such an object. You could even use this object 
in order to create another one:

    return Candidate_1 : T (Size => 1) do
       Size := Candidate_1.Get_Required_Size;
       raise Try_Again;
    end return;
exception
    when Try_Again =>
       return Candidate_2 : T (Size => Size) do
           ...
       end return;

Since Ada lacks proper user-defined constructors one cannot claim that 
Candidate_1 was not fully constructed. Even its Initialize was through.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11 18:17     ` Bob Duff
  2016-01-11 21:10       ` Dmitry A. Kazakov
@ 2016-01-12 12:41       ` Brian Drummond
  2016-01-13 20:18       ` Jacob Sparre Andersen
  2 siblings, 0 replies; 27+ messages in thread
From: Brian Drummond @ 2016-01-12 12:41 UTC (permalink / raw)


On Mon, 11 Jan 2016 13:17:20 -0500, Bob Duff wrote:

> Brian Drummond <brian@shapes.demon.co.uk> writes:
> 
>> Somehow I expected "extended return" to allocate space and "build in
>> place" during the execution of the return statement.
> 
> Build-in-place is done for return of immutably-limited types,
> whether or not the extended return syntax is used.
> 
> For nonlimited types, the compiler is free to use build-in-place if it
> can prove that it's equivalent to return-by-copy.  It's often not
> equivalent.  For example:
> 
>     X := Init;
> 
> What if Init raises an exception after partly modifying the result? In
> that case X should not be modified, so the result object of Init and X
> had better not be at the same address.

Thank you (and Simon and Brad) for the clarification. 

I had not considered the ramifications in the case of exceptions, and the 
impracticability of rolling back a partially modified object...

-- Brian


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-12  9:33           ` Dmitry A. Kazakov
@ 2016-01-12 20:21             ` Randy Brukardt
  2016-01-12 21:05               ` Dmitry A. Kazakov
  0 siblings, 1 reply; 27+ messages in thread
From: Randy Brukardt @ 2016-01-12 20:21 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message 
news:n72hag$bjq$1@gioia.aioe.org...
> On 12/01/2016 00:44, Randy Brukardt wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>> news:n715oq$fae$1@gioia.aioe.org...
>>> On 2016-01-11 19:17, Bob Duff wrote:
>>>> Brian Drummond <brian@shapes.demon.co.uk> writes:
>>>>
>>>>> Somehow I expected "extended return" to allocate space and "build in
>>>>> place" during the execution of the return statement.
>>>>
>>>> Build-in-place is done for return of immutably-limited types,
>>>> whether or not the extended return syntax is used.
>>>
>>> But you can leave one return statement on an exception, catch the
>>> exception, and then return through another return statement, with other
>>> discriminants and even other type of the result (if it is class-wide).
>>>
>>> Therefore the result can be potentially allocated and reallocated any
>>> number of times. In which sense is this behavior 'in-place'?
>>
>> "Build-in-place" means simply that there is no temporary object, the 
>> object
>> is created directly in its final resting place (that is the memory where 
>> it
>> will live during its existence).
>>
>> Whatever amount of effort it takes to figure out that final resting place 
>> is
>> not part of the equation.
>
> Yes, but you said 'no temporary object' which is untrue because a return 
> statement creates exactly such an object. You could even use this object 
> in order to create another one:
>
>    return Candidate_1 : T (Size => 1) do
>       Size := Candidate_1.Get_Required_Size;
>       raise Try_Again;
>    end return;
> exception
>    when Try_Again =>
>       return Candidate_2 : T (Size => Size) do
>           ...
>       end return;
>
> Since Ada lacks proper user-defined constructors one cannot claim that 
> Candidate_1 was not fully constructed. Even its Initialize was through.

But this is *not* a temporary object. The final object might change identity 
in such a scenario, but Candidate_1 and Candidate_2 are the *same* object, 
with different properties. The various limitations on using a function call 
as the parent part of an extension aggregate exist specifically because of 
this effect.

For a inherently limited type (the kind that requires build-in-place), the 
location of the object is part of its identity, argubly the most important 
part.

(I realize that you might be defining "object" in your own way, which as 
always is irrelevant; I'm using the definition of "object" and "temporary 
object" as used in the Ada Standard.)

                                          Randy.



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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-12 20:21             ` Randy Brukardt
@ 2016-01-12 21:05               ` Dmitry A. Kazakov
  2016-01-13  0:02                 ` Robert A Duff
  0 siblings, 1 reply; 27+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-12 21:05 UTC (permalink / raw)


On 2016-01-12 21:21, Randy Brukardt wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
> news:n72hag$bjq$1@gioia.aioe.org...
>> On 12/01/2016 00:44, Randy Brukardt wrote:
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>>> news:n715oq$fae$1@gioia.aioe.org...
>>>> On 2016-01-11 19:17, Bob Duff wrote:
>>>>> Brian Drummond <brian@shapes.demon.co.uk> writes:
>>>>>
>>>>>> Somehow I expected "extended return" to allocate space and "build in
>>>>>> place" during the execution of the return statement.
>>>>>
>>>>> Build-in-place is done for return of immutably-limited types,
>>>>> whether or not the extended return syntax is used.
>>>>
>>>> But you can leave one return statement on an exception, catch the
>>>> exception, and then return through another return statement, with other
>>>> discriminants and even other type of the result (if it is class-wide).
>>>>
>>>> Therefore the result can be potentially allocated and reallocated any
>>>> number of times. In which sense is this behavior 'in-place'?
>>>
>>> "Build-in-place" means simply that there is no temporary object, the
>>> object
>>> is created directly in its final resting place (that is the memory where
>>> it
>>> will live during its existence).
>>>
>>> Whatever amount of effort it takes to figure out that final resting place
>>> is
>>> not part of the equation.
>>
>> Yes, but you said 'no temporary object' which is untrue because a return
>> statement creates exactly such an object. You could even use this object
>> in order to create another one:
>>
>>     return Candidate_1 : T (Size => 1) do
>>        Size := Candidate_1.Get_Required_Size;
>>        raise Try_Again;
>>     end return;
>> exception
>>     when Try_Again =>
>>        return Candidate_2 : T (Size => Size) do
>>            ...
>>        end return;
>>
>> Since Ada lacks proper user-defined constructors one cannot claim that
>> Candidate_1 was not fully constructed. Even its Initialize was through.
>
> But this is *not* a temporary object. The final object might change identity
> in such a scenario, but Candidate_1 and Candidate_2 are the *same* object,
> with different properties.

What makes two objects "same"? Especially if:

1. none of them exists at the same time as another
2. one of them is already finalized
3. they have different specific types, discriminats, components

If T were not limited would they be same too?

> For a inherently limited type (the kind that requires build-in-place), the
> location of the object is part of its identity, argubly the most important
> part.

What is "location"?

Does RM specifically require the implementation to have 
Candidate_1'Address = Candidate_2'Address?

The following program -------------------------->
with Ada.Text_IO; use Ada.Text_IO;
with System.Storage_Elements;  use System.Storage_Elements;

procedure Test_Return is
    package P is
       type T is tagged limited null record;
    end P;
    package Q is
       type S (L : Positive) is new P.T with record
          X : String (1..L);
       end record;
    end Q;
    function Create return P.T'Class is
    begin
       return Candidate_1 : P.T do
          Put_Line (Integer_Address'Image (To_Integer 
(Candidate_1'Address)));
          raise Constraint_Error;
       end return;
    exception
       when Constraint_Error =>
          return Candidate_2 : Q.S (1000) do
             Put_Line (Integer_Address'Image (To_Integer 
(Candidate_2'Address)));
          end return;
    end Create;	
    X : P.T'Class := Create;
begin
    null;
end Test_Return;
<----------------------------------------
prints:
 >test_return
  4454256
  4454272

Is GNAT at fault? Maybe "location" is not the memory address? And

4. The same object allocated at two different memory addresses?

Finally, I know that you don't consider RM subject to mathematical 
logic, but in logic partial identity is just meaningless. E.g. you may 
say that the highest order digit is the most important part of number, 
but that does not imply two numbers same when their highest order digits 
are same.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-12 21:05               ` Dmitry A. Kazakov
@ 2016-01-13  0:02                 ` Robert A Duff
  2016-01-13  8:31                   ` Dmitry A. Kazakov
  0 siblings, 1 reply; 27+ messages in thread
From: Robert A Duff @ 2016-01-13  0:02 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:

> On 2016-01-12 21:21, Randy Brukardt wrote:
>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>> news:n72hag$bjq$1@gioia.aioe.org...
>>> On 12/01/2016 00:44, Randy Brukardt wrote:
>>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>>>> news:n715oq$fae$1@gioia.aioe.org...
>>>>> On 2016-01-11 19:17, Bob Duff wrote:
>>>>>> Build-in-place is done for return of immutably-limited types,
>>>>>> whether or not the extended return syntax is used.
>>>>>
>>>>> But you can leave one return statement on an exception, catch the
>>>>> exception, and then return through another return statement, with other
>>>>> discriminants and even other type of the result (if it is class-wide).

Yes.  Or you can 'goto' or 'exit' out of an extended return.

>>>>> Therefore the result can be potentially allocated and reallocated any
>>>>> number of times. In which sense is this behavior 'in-place'?

"In place" means that the returned object created by the return
statement that actually returns is the same object as the one at the
call site.  If the return statement raises an exception, then it doesn't
actually return.

Note that this point has nothing to do with extended_return syntax.
An old fashioned "return <expression>;" can raise an exception,
and be handled, and then do a different return.  The syntax doesn't
matter; build-in-place is controlled by "immutably limited".

>>>     return Candidate_1 : T (Size => 1) do
>>>        Size := Candidate_1.Get_Required_Size;
>>>        raise Try_Again;
>>>     end return;
>>> exception
>>>     when Try_Again =>
>>>        return Candidate_2 : T (Size => Size) do
>>>            ...
>>>        end return;
>>>
>>> Since Ada lacks proper user-defined constructors one cannot claim that
>>> Candidate_1 was not fully constructed. Even its Initialize was through.
>>
>> But this is *not* a temporary object. The final object might change identity
>> in such a scenario, but Candidate_1 and Candidate_2 are the *same* object,
>> with different properties.

No, that's not right.  Candidate_1 is an object created by the first
return statement, and finalized during "raise Try_Again;".  It no
longer exists when Candidate_2 is created.  If Candidate_2 is returned
(there's no more raise/goto/whatever in there), then Candidate_2
is the same object as the one created at the call site.

> What makes two objects "same"? ...

The RM is clear on that point, and it is clear that the two candidates
are not the same object.

> Does RM specifically require the implementation to have
> Candidate_1'Address = Candidate_2'Address?

No, the RM says almost nothing about 'Address.  A garbage collector
could move objects around.  Access values are equal if and only if
they designate the same object (even in the presence of GC), but
Addresses are not (necessarily) so well behaved.

> The following program -------------------------->
> with Ada.Text_IO; use Ada.Text_IO;
> with System.Storage_Elements;  use System.Storage_Elements;
>
> procedure Test_Return is
>    package P is
>       type T is tagged limited null record;
>    end P;
>    package Q is
>       type S (L : Positive) is new P.T with record
>          X : String (1..L);
>       end record;
>    end Q;
>    function Create return P.T'Class is
>    begin
>       return Candidate_1 : P.T do
>          Put_Line (Integer_Address'Image (To_Integer
> (Candidate_1'Address)));
>          raise Constraint_Error;
>       end return;
>    exception
>       when Constraint_Error =>
>          return Candidate_2 : Q.S (1000) do
>             Put_Line (Integer_Address'Image (To_Integer
> (Candidate_2'Address)));
>          end return;
>    end Create;	
>    X : P.T'Class := Create;
> begin
>    null;
> end Test_Return;
> <----------------------------------------
> prints:
>>test_return
>  4454256
>  4454272
>
> Is GNAT at fault? Maybe "location" is not the memory address? And

The RM doesn't define "location".  And the rules about 'Address are
pretty loose.  And GNAT is correct here.

I think what Randy meant to say is that X (declared in Test_Return)
is the same object as Candidate_2.  That is what is required by the
RM, and it's what "build in place" means.  In GNAT, if you printed
X'Address it would match Candidate_2'Address, but that's not required
by the RM.  The two candidates are not the same object, as you
correctly noted.

If you had said:

    type Acc is access P.T'Class;
    X : Acc := new P.T'Class'(Create);

then Candidate_1 would get allocated on the heap, then finalized and
deallocated, then Candidate_2 would get allocated on the heap, and
X.all would end up being the same object as Candidate_2.

But keep in mind that the above example is quite unusual.  The normal
case is to have exactly one extended return.  And the size of the thing
is known at compile time, or at least known at the call site, so
the caller can allocate the result (stack, heap, whatever), and
pass the address of that to the function.

> 4. The same object allocated at two different memory addresses?
>
> Finally, I know that you don't consider RM subject to mathematical
> logic, ...

Now, now, that's just sniping.  We try to make the RM correct,
but we make mistakes.

- Bob


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13  0:02                 ` Robert A Duff
@ 2016-01-13  8:31                   ` Dmitry A. Kazakov
  2016-01-13  9:01                     ` Georg Bauhaus
                                       ` (2 more replies)
  0 siblings, 3 replies; 27+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-13  8:31 UTC (permalink / raw)


On 13/01/2016 01:02, Robert A Duff wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>
>> On 2016-01-12 21:21, Randy Brukardt wrote:
>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>>> news:n72hag$bjq$1@gioia.aioe.org...
>>>> On 12/01/2016 00:44, Randy Brukardt wrote:
>>>>> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> wrote in message
>>>>> news:n715oq$fae$1@gioia.aioe.org...
>>>>>> On 2016-01-11 19:17, Bob Duff wrote:
>>>>>>> Build-in-place is done for return of immutably-limited types,
>>>>>>> whether or not the extended return syntax is used.
>>>>>>
>>>>>> But you can leave one return statement on an exception, catch the
>>>>>> exception, and then return through another return statement, with other
>>>>>> discriminants and even other type of the result (if it is class-wide).
>
> Yes.  Or you can 'goto' or 'exit' out of an extended return.
>
>>>>>> Therefore the result can be potentially allocated and reallocated any
>>>>>> number of times. In which sense is this behavior 'in-place'?
>
> "In place" means that the returned object created by the return
> statement that actually returns is the same object as the one at the
> call site.

It is rather a useless definition, because it would apply to the objects 
returned by-copy as well. At some point *some* object created as a 
result of a call to the callee becomes the object of the caller.

To my understanding Randy's definition tried to address this by claiming 
absence of intermediate objects (as a generalization of the notion of 
copying).

> Note that this point has nothing to do with extended_return syntax.

Right. Also neither and no definition will work ever. Whichever syntax 
"same" object cannot be "returned".

> The RM doesn't define "location".  And the rules about 'Address are
> pretty loose.  And GNAT is correct here.

I didn't doubt it. The example was for the sake of argument. If RM 
attempted to define "same" location in a useful way that would make 
reasonable implementations impossible.

> Now, now, that's just sniping.

Yes, yes, I fired back at an attempt to evade discussion by the game of 
definitions. (:-))

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13  8:31                   ` Dmitry A. Kazakov
@ 2016-01-13  9:01                     ` Georg Bauhaus
  2016-01-13 14:45                     ` J-P. Rosen
  2016-01-13 16:03                     ` Robert A Duff
  2 siblings, 0 replies; 27+ messages in thread
From: Georg Bauhaus @ 2016-01-13  9:01 UTC (permalink / raw)


On 13.01.16 09:31, Dmitry A. Kazakov wrote:
>
>> Now, now, that's just sniping.
>
> Yes, yes, I fired back at an attempt to evade discussion by the game of definitions. (:-))

Of mathematical logic?

SCNR

-- 
"HOTDOGS ARE NOT BOOKMARKS"
Springfield Elementary teaching staff

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13  8:31                   ` Dmitry A. Kazakov
  2016-01-13  9:01                     ` Georg Bauhaus
@ 2016-01-13 14:45                     ` J-P. Rosen
  2016-01-13 20:09                       ` Dmitry A. Kazakov
  2016-01-13 16:03                     ` Robert A Duff
  2 siblings, 1 reply; 27+ messages in thread
From: J-P. Rosen @ 2016-01-13 14:45 UTC (permalink / raw)


Le 13/01/2016 09:31, Dmitry A. Kazakov a écrit :
> On 13/01/2016 01:02, Robert A Duff wrote:
>> "In place" means that the returned object created by the return
>> statement that actually returns is the same object as the one at the
>> call site.
> 
> It is rather a useless definition, because it would apply to the objects
> returned by-copy as well. At some point *some* object created as a
> result of a call to the callee becomes the object of the caller.
> 
OK, let's state it differently. The called function builds its result
"somewhere". That "somewhere" is what will be used by the caller as the
variable being initialized.

It is perfectly OK if the function starts building the result in
"somewhere_1", then decides to switch to "somewhere_2", as long as the
eventual "somewhere" is what is used by the caller, /without copy/.

-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13  8:31                   ` Dmitry A. Kazakov
  2016-01-13  9:01                     ` Georg Bauhaus
  2016-01-13 14:45                     ` J-P. Rosen
@ 2016-01-13 16:03                     ` Robert A Duff
  2016-01-13 19:59                       ` Dmitry A. Kazakov
  2 siblings, 1 reply; 27+ messages in thread
From: Robert A Duff @ 2016-01-13 16:03 UTC (permalink / raw)


"Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:

> It is rather a useless definition, because it would apply to the objects
> returned by-copy as well. At some point *some* object created as a
> result of a call to the callee becomes the object of the caller.

No, nonlimited objects work differently.  Consider:

  package P is

     type Doubly_Linked_List;
     type Node_Ptr is access all Doubly_Linked_List;
     type Doubly_Linked_List is limited record
        Next, Prev: Node_Ptr;
        Payload: Integer;
     end record;

     function Empty_List return Doubly_Linked_List;

     Global: Node_Ptr;

  end P;

  package body P is

     function Empty_List return Doubly_Linked_List is
     begin
        return Result: aliased Doubly_Linked_List do
           Result.Next := Result'Unchecked_Access;
           Result.Prev := Result'Unchecked_Access;
           Global := Result'Unchecked_Access;
        end return;
     end Empty_List;

  end P;

  with Text_IO; use Text_IO;
  procedure P.Main is
     My_List: aliased Doubly_Linked_List := Empty_List;
  begin
     Put_Line(Boolean'(Global = My_List'Unchecked_Access)'Img);
     Put_Line(Boolean'(My_List.Next = My_List'Unchecked_Access)'Img);
     Put_Line(Boolean'(My_List.Prev = My_List'Unchecked_Access)'Img);
  end P.Main;

The example must print TRUE three times.  In Empty_List, we set three
pointers pointing to Result.  After the call they point to My_List.
That's because My_List and Result are the same object.

If the implementation chooses to put My_List at a different location
than Result, and copy Result into My_List, it would have to update
those pointers, which is not easy.  The intended implementation is
to make sure that My_List and Result are at the same address.
My_List is of known size, so it is allocated at the declaration,
and its address is passed to Empty_List, so it knows where
to put Result.  If the size were not known at the call site,
things would be more complicated -- it passes information about
where to allocate (on the stack, on the global heap, in some
user-defined storage pool, ...), and the function would
do the allocation once it knows the size, and passes the
address back to the caller.

If we erase "limited", then the example is illegal -- Result can't be
aliased.  If that were allowed, then all three pointers would be
dangling pointers, and the Put_Lines would all be erroneous.
That's because Result and My_List are NOT the same object
in the nonlimited case, and Result is gone by the time the
Put_Lines are executed.

> To my understanding Randy's definition tried to address this by claiming
> absence of intermediate objects (as a generalization of the notion of
> copying).
>
>> Note that this point has nothing to do with extended_return syntax.
>
> Right. Also neither and no definition will work ever. Whichever syntax
> "same" object cannot be "returned".

Two access values are "=" if and only if they designate the same
object.  See example above.  QED.

>> The RM doesn't define "location".  And the rules about 'Address are
>> pretty loose.  And GNAT is correct here.
>
> I didn't doubt it. The example was for the sake of argument. If RM
> attempted to define "same" location in a useful way that would make
> reasonable implementations impossible.

Agreed.

>> Now, now, that's just sniping.
>
> Yes, yes, I fired back at an attempt to evade discussion by the game of
> definitions. (:-))

;-)

- Bob

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13 16:03                     ` Robert A Duff
@ 2016-01-13 19:59                       ` Dmitry A. Kazakov
  2016-01-14 10:04                         ` Georg Bauhaus
  0 siblings, 1 reply; 27+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-13 19:59 UTC (permalink / raw)


On 2016-01-13 17:03, Robert A Duff wrote:
> "Dmitry A. Kazakov" <mailbox@dmitry-kazakov.de> writes:
>
>> It is rather a useless definition, because it would apply to the objects
>> returned by-copy as well. At some point *some* object created as a
>> result of a call to the callee becomes the object of the caller.
>
> No, nonlimited objects work differently.  Consider:
>
>    package P is
>
>       type Doubly_Linked_List;
>       type Node_Ptr is access all Doubly_Linked_List;
>       type Doubly_Linked_List is limited record
>          Next, Prev: Node_Ptr;
>          Payload: Integer;
>       end record;
>
>       function Empty_List return Doubly_Linked_List;
>
>       Global: Node_Ptr;
>
>    end P;
>
>    package body P is
>
>       function Empty_List return Doubly_Linked_List is
>       begin
>          return Result: aliased Doubly_Linked_List do
>             Result.Next := Result'Unchecked_Access;
>             Result.Prev := Result'Unchecked_Access;
>             Global := Result'Unchecked_Access;
>          end return;
>       end Empty_List;
>
>    end P;
>
>    with Text_IO; use Text_IO;
>    procedure P.Main is
>       My_List: aliased Doubly_Linked_List := Empty_List;
>    begin
>       Put_Line(Boolean'(Global = My_List'Unchecked_Access)'Img);
>       Put_Line(Boolean'(My_List.Next = My_List'Unchecked_Access)'Img);
>       Put_Line(Boolean'(My_List.Prev = My_List'Unchecked_Access)'Img);
>    end P.Main;
>
> The example must print TRUE three times.  In Empty_List, we set three
> pointers pointing to Result.  After the call they point to My_List.
> That's because My_List and Result are the same object.

Yes, but your definition still apply to all types. Some object must 
travel across the callee-caller border. That last object remains same 
regardless limited or not.

> If we erase "limited", then the example is illegal -- Result can't be
> aliased.  If that were allowed, then all three pointers would be
> dangling pointers, and the Put_Lines would all be erroneous.
> That's because Result and My_List are NOT the same object
> in the nonlimited case, and Result is gone by the time the
> Put_Lines are executed.

You could make it Controlled and fix pointers in Adjust. Your program 
will not notice a difference.

>> To my understanding Randy's definition tried to address this by claiming
>> absence of intermediate objects (as a generalization of the notion of
>> copying).
>>
>>> Note that this point has nothing to do with extended_return syntax.
>>
>> Right. Also neither and no definition will work ever. Whichever syntax
>> "same" object cannot be "returned".
>
> Two access values are "=" if and only if they designate the same
> object.

You can have different access values pointing same object (e.g. of two 
different access types) and, possibly, same access values pointing 
different objects (I believe Ada permits "near" access values for a 
segmented memory machine, then there is Unchecked_Union etc)

> See example above.  QED.

Sure, some object is always returned and every object is always same to 
itself. This is not sufficient to define useful "in-place". In my view a 
definition must have the place known at the caller's context. E.g. to 
have the object:

1. Allocated at a specific machine address
2. Allocated by an allocator in the specific memory pool
3. Allocated as a specific component of a container object
4. Allocated as a parent of a derived type object

The problem of returned object is that it conflates allocation with 
initialization.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13 14:45                     ` J-P. Rosen
@ 2016-01-13 20:09                       ` Dmitry A. Kazakov
  2016-01-14  9:04                         ` J-P. Rosen
  0 siblings, 1 reply; 27+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-13 20:09 UTC (permalink / raw)


On 2016-01-13 15:45, J-P. Rosen wrote:
> Le 13/01/2016 09:31, Dmitry A. Kazakov a écrit :
>> On 13/01/2016 01:02, Robert A Duff wrote:
>>> "In place" means that the returned object created by the return
>>> statement that actually returns is the same object as the one at the
>>> call site.
>>
>> It is rather a useless definition, because it would apply to the objects
>> returned by-copy as well. At some point *some* object created as a
>> result of a call to the callee becomes the object of the caller.
>>
> OK, let's state it differently. The called function builds its result
> "somewhere". That "somewhere" is what will be used by the caller as the
> variable being initialized.

A non-limited result is built "somewhere", just the same. I think 
in-place must be a definite place, not just "somewhere".

> It is perfectly OK if the function starts building the result in
> "somewhere_1", then decides to switch to "somewhere_2", as long as the
> eventual "somewhere" is what is used by the caller, /without copy/.

"Without copy" requires a definition too. IMO, it must mean that no type 
operations are called otherwise than on the final result (function 
arguments do not count). For example, Initialize must be called strictly 
once.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-11 18:17     ` Bob Duff
  2016-01-11 21:10       ` Dmitry A. Kazakov
  2016-01-12 12:41       ` Brian Drummond
@ 2016-01-13 20:18       ` Jacob Sparre Andersen
  2016-01-14  1:31         ` Robert A Duff
  2 siblings, 1 reply; 27+ messages in thread
From: Jacob Sparre Andersen @ 2016-01-13 20:18 UTC (permalink / raw)


Bob Duff <bobduff@theworld.com> writes:
> Brian Drummond <brian@shapes.demon.co.uk> writes:

> For nonlimited types, the compiler is free to use build-in-place if it
> can prove that it's equivalent to return-by-copy.  It's often not
> equivalent.  For example:
>
>     X := Init;
>
> What if Init raises an exception after partly modifying the result?
> In that case X should not be modified, so the result object of Init
> and X had better not be at the same address.

But if:

a) There is no exception handler between the assignment and leaving the
   scope of X, build-in-place is okay (but impractical)?

b) The compiler can prove that no exceptions can occur after the first
   modification of the result inside Init, build-in-place is okay?

Greetings,

Jacob
-- 
"The butcher backed into the meat grinder and got a little behind
 in his work."

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13 20:18       ` Jacob Sparre Andersen
@ 2016-01-14  1:31         ` Robert A Duff
  0 siblings, 0 replies; 27+ messages in thread
From: Robert A Duff @ 2016-01-14  1:31 UTC (permalink / raw)


Jacob Sparre Andersen <sparre@nbi.dk> writes:

> Bob Duff <bobduff@theworld.com> writes:
>> Brian Drummond <brian@shapes.demon.co.uk> writes:
>
>> For nonlimited types, the compiler is free to use build-in-place if it
>> can prove that it's equivalent to return-by-copy.  It's often not
>> equivalent.  For example:
>>
>>     X := Init;
>>
>> What if Init raises an exception after partly modifying the result?
>> In that case X should not be modified, so the result object of Init
>> and X had better not be at the same address.
>
> But if:
>
> a) There is no exception handler between the assignment and leaving the
>    scope of X, build-in-place is okay (but impractical)?

Yes, I think that's OK.  I think it's also practical in some cases.

You also have to worry about the case where Init can see X.
X isn't supposed to (appear to be) changed until return from Init.

> b) The compiler can prove that no exceptions can occur after the first
>    modification of the result inside Init, build-in-place is okay?

Yes.  But it might be hard to prove that Storage_Error won't happen,
for example.

- Bob

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13 20:09                       ` Dmitry A. Kazakov
@ 2016-01-14  9:04                         ` J-P. Rosen
  2016-01-14  9:47                           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 27+ messages in thread
From: J-P. Rosen @ 2016-01-14  9:04 UTC (permalink / raw)


Le 13/01/2016 21:09, Dmitry A. Kazakov a écrit :
>> OK, let's state it differently. The called function builds its result
>> "somewhere". That "somewhere" is what will be used by the caller as the
>> variable being initialized.
> 
> A non-limited result is built "somewhere", just the same. I think
> in-place must be a definite place, not just "somewhere".
In the non-limited case, copying is allowed (although not required), so
there is no issue. What I mean is that it can work the other way round
from your mental model: you think of the caller allocating the result
space, and the function using that space; but it can be the function
that choses where to build the result, and then the caller using that
space for the object. It may not be the usual stack model, but that's
irrelevant (we already know that a secondary stack may be used in some
cases).

>> It is perfectly OK if the function starts building the result in
>> "somewhere_1", then decides to switch to "somewhere_2", as long as the
>> eventual "somewhere" is what is used by the caller, /without copy/.
> 
> "Without copy" requires a definition too. IMO, it must mean that no type
> operations are called otherwise than on the final result (function
> arguments do not count). For example, Initialize must be called strictly
> once.
"Without copy" means that the value constructed and returned by the
function should not be moved to a different memory  location. What else?

-- 
J-P. Rosen
Adalog
2 rue du Docteur Lombard, 92441 Issy-les-Moulineaux CEDEX
Tel: +33 1 45 29 21 52, Fax: +33 1 45 29 25 00
http://www.adalog.fr


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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-14  9:04                         ` J-P. Rosen
@ 2016-01-14  9:47                           ` Dmitry A. Kazakov
  0 siblings, 0 replies; 27+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-14  9:47 UTC (permalink / raw)


On 14/01/2016 10:04, J-P. Rosen wrote:
> Le 13/01/2016 21:09, Dmitry A. Kazakov a écrit :
>>> OK, let's state it differently. The called function builds its result
>>> "somewhere". That "somewhere" is what will be used by the caller as the
>>> variable being initialized.
>>
>> A non-limited result is built "somewhere", just the same. I think
>> in-place must be a definite place, not just "somewhere".
> In the non-limited case, copying is allowed (although not required), so
> there is no issue. What I mean is that it can work the other way round
> from your mental model: you think of the caller allocating the result
> space, and the function using that space; but it can be the function
> that choses where to build the result, and then the caller using that
> space for the object. It may not be the usual stack model, but that's
> irrelevant (we already know that a secondary stack may be used in some
> cases).

For many (most, actually) important cases this is relevant, e.g. for an 
initialized limited component of a container object or a parent of. 
There is no way to do this without exposing all innards and even with 
exposing them chances are very high it won't work anyway.

>>> It is perfectly OK if the function starts building the result in
>>> "somewhere_1", then decides to switch to "somewhere_2", as long as the
>>> eventual "somewhere" is what is used by the caller, /without copy/.
>>
>> "Without copy" requires a definition too. IMO, it must mean that no type
>> operations are called otherwise than on the final result (function
>> arguments do not count). For example, Initialize must be called strictly
>> once.
> "Without copy" means that the value constructed and returned by the
> function should not be moved to a different memory  location.
> What else?

You mean "object constructed" (the object refers to a value). I think 
that "copy" should be stated in terms of operations defined on the 
object. If the client cannot tell difference through legal operation 
then it is OK. Therefore the requirement that Initialize and other 
elaboration stuff must be performed once.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-13 19:59                       ` Dmitry A. Kazakov
@ 2016-01-14 10:04                         ` Georg Bauhaus
  2016-01-14 13:42                           ` Dmitry A. Kazakov
  0 siblings, 1 reply; 27+ messages in thread
From: Georg Bauhaus @ 2016-01-14 10:04 UTC (permalink / raw)


On 13.01.16 20:59, Dmitry A. Kazakov wrote:
> Sure, some object is always returned and every object is always same to itself. This is not sufficient to define useful "in-place". In my view a definition must have the place known at the caller's context. E.g. to have the object:
>
> 1. Allocated at a specific machine address
> 2. Allocated by an allocator in the specific memory pool
> 3. Allocated as a specific component of a container object
> 4. Allocated as a parent of a derived type object
>
> The problem of returned object is that it conflates allocation with initialization.

If Ada will have a way for the caller to express the placement of new objects
to be constructed in situ by functions, might we then go back to just
return-by-reference in the hope of abandoning "return-by-access"?

I.e., in this case, the caller could request that a limited object
be placed, e.g., on the stack, after having been initialized by the function.
Or that it become a named component of some other object.

-- 
"HOTDOGS ARE NOT BOOKMARKS"
Springfield Elementary teaching staff

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

* Re: Why is the destructor called multiple times after I declare an object?
  2016-01-14 10:04                         ` Georg Bauhaus
@ 2016-01-14 13:42                           ` Dmitry A. Kazakov
  0 siblings, 0 replies; 27+ messages in thread
From: Dmitry A. Kazakov @ 2016-01-14 13:42 UTC (permalink / raw)


On 14/01/2016 11:04, Georg Bauhaus wrote:
> On 13.01.16 20:59, Dmitry A. Kazakov wrote:
>> Sure, some object is always returned and every object is always same
>> to itself. This is not sufficient to define useful "in-place". In my
>> view a definition must have the place known at the caller's context.
>> E.g. to have the object:
>>
>> 1. Allocated at a specific machine address
>> 2. Allocated by an allocator in the specific memory pool
>> 3. Allocated as a specific component of a container object
>> 4. Allocated as a parent of a derived type object
>>
>> The problem of returned object is that it conflates allocation with
>> initialization.
>
> If Ada will have a way for the caller to express the placement of new
> objects to be constructed in situ by functions,

By procedures, of course. Initializer must a procedure.

> might we then go back to just
> return-by-reference in the hope of abandoning "return-by-access"?

There is a simpler and cleaner way. What is needed is user-defined 
procedures implementing record/array component read and update access as 
primitive operations. No need to implement the syntax sugar

    X.M := F (...)

literally as two separate evaluations of X.M and F(...) with inventing 
some intermediate objects to pass to some virtual assignment meaningless 
for limited types anyway. It should be

    Set_M (X, ...);

with lots of problems with limited and indefinite types eliminated.

-- 
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de


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

end of thread, other threads:[~2016-01-14 13:42 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-01-11  1:37 Why is the destructor called multiple times after I declare an object? Andrew Shvets
2016-01-11  2:18 ` Jeffrey R. Carter
2016-01-11  3:35   ` Andrew Shvets
2016-01-11 17:02     ` Brian Drummond
2016-01-11 16:29   ` Brian Drummond
2016-01-11 17:20     ` Simon Wright
2016-01-11 18:17     ` Bob Duff
2016-01-11 21:10       ` Dmitry A. Kazakov
2016-01-11 23:44         ` Randy Brukardt
2016-01-12  9:33           ` Dmitry A. Kazakov
2016-01-12 20:21             ` Randy Brukardt
2016-01-12 21:05               ` Dmitry A. Kazakov
2016-01-13  0:02                 ` Robert A Duff
2016-01-13  8:31                   ` Dmitry A. Kazakov
2016-01-13  9:01                     ` Georg Bauhaus
2016-01-13 14:45                     ` J-P. Rosen
2016-01-13 20:09                       ` Dmitry A. Kazakov
2016-01-14  9:04                         ` J-P. Rosen
2016-01-14  9:47                           ` Dmitry A. Kazakov
2016-01-13 16:03                     ` Robert A Duff
2016-01-13 19:59                       ` Dmitry A. Kazakov
2016-01-14 10:04                         ` Georg Bauhaus
2016-01-14 13:42                           ` Dmitry A. Kazakov
2016-01-12 12:41       ` Brian Drummond
2016-01-13 20:18       ` Jacob Sparre Andersen
2016-01-14  1:31         ` Robert A Duff
2016-01-12  0:43     ` Jeffrey R. Carter

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