www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Presence of struct destructor makes lvalue?

reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
The following program has two assignments from two rvalues:

import std.stdio;
import std.string;

// version = destructor_defined;

struct S
{
     int i;

     string info() const
     {
         return format("%s(%s)", &i, i);
     }

     void opAssign(S rhs)
     {
         writefln("%s from Rvalue %s", this.info(), rhs.info());
     }

     void opAssign(ref S rhs)
     {
         writefln("%s from Lvalue %s", this.info(), rhs.info());
     }

     version (destructor_defined)
     {
         ~this()
         {
             writefln("destroying %s", this.info());
         }
     }
}

S foo(int i)
{
     return S(i);
}

void main()
{
     auto s = S(1);

     // Assignment from two kinds of rvalues
     s = foo(2);
     s = S(3);
}

The output indicates that the two assignments go to the "rvalue 
assignment operator":

7FFF36AA0B20(1) from Rvalue 7FFF36AA0B24(2)
7FFF36AA0B20(1) from Rvalue 7FFF36AA0B28(3)

However, when S.~this() is defined (by e.g. uncommenting the 'version =' 
line), the second assignment involves an lvalue:

7FFF9571E7F8(1) from Rvalue 7FFF9571E750(2)
destroying 7FFF9571E750(2)
7FFF9571E7F8(1) from Lvalue 7FFF9571E804(3)  <-- HERE
destroying 7FFF9571E804(3)
destroying 7FFF9571E7F8(1)

I suspect this has something to do with the compiler generated 
assignment logic:

   http://dlang.org/struct.html

[quote]
Struct assignment t=s is defined to be semantically equivalent to:

t.opAssign(s);

where opAssign is a member function of S:

S* opAssign(ref const S s)
{   ... bitcopy *this into tmp ...
     ... bitcopy s into *this ...
     ... call destructor on tmp ...
     return this;
}
[/quote]

It is not clear how the presence of ~this() changes the semantics. Bug?

Thank you,
Ali
Apr 08 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
DMD often inserts temporaries when they are unexpected (or users 
do not expect when they should).

D main function in destructor case is rewritten as:

    S s = S(1);
    s.opAssign(foo(2));
    s.opAssign((S __sl1779 = S(3); , __sl1779))

Hence, ref version is taken in account because '__sl1779' is a 
good lvalue.

Troubles seems to come from 
https://github.com/D-Programming-Language/dmd/blob/master/src/expression.c#L4203
Apr 08 2013
parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 04/08/2013 12:24 PM, Maxim Fomin wrote:

 DMD often inserts temporaries when they are unexpected (or users do not
 expect when they should).

 D main function in destructor case is rewritten as:

     S s = S(1);
     s.opAssign(foo(2));
     s.opAssign((S __sl1779 = S(3); , __sl1779))
Have you manually typed those lines or is there a tool that outputs that "preprocessor" ;) output?
 Hence, ref version is taken in account because '__sl1779' is a good 
lvalue.
 Troubles seems to come from
 
https://github.com/D-Programming-Language/dmd/blob/master/sr /expression.c#L4203

Thank you very much for finding the related line. :) Quoting:

Expression *StructLiteralExp::semantic(Scope *sc)
{ Expression *e;

// ...

     /* If struct requires a destructor, rewrite as:
      * (S tmp = S()),tmp
      * so that the destructor can be hung on tmp.
      */
     if (sd->dtor && sc->func)
     {
         Identifier *idtmp = Lexer::uniqueId("__sl");
         VarDeclaration *tmp = new VarDeclaration(loc, type, idtmp, new 
ExpInitializer(0, this));
         tmp->storage_class |= STCctfe;
         Expression *ae = new DeclarationExp(loc, tmp);
         Expression *e = new CommaExp(loc, ae, new VarExp(loc, tmp));
         e = e->semantic(sc);
         return e;
     }

     return this;
}

Is it because destructors cannot be "hung" on literals?

It is inconsistent that function-returned rvalues do not behave the same 
as literal-rvalues.

Ali
Apr 08 2013
parent reply kenji hara <k.hara.pg gmail.com> writes:
This is definitely a bug.

http://d.puremagic.com/issues/show_bug.cgi?id=3D9907

Kenji Hara

2013/4/9 Ali =C3=87ehreli <acehreli yahoo.com>

 On 04/08/2013 12:24 PM, Maxim Fomin wrote:

 DMD often inserts temporaries when they are unexpected (or users do not
 expect when they should).

 D main function in destructor case is rewritten as:

     S s =3D S(1);
     s.opAssign(foo(2));
     s.opAssign((S __sl1779 =3D S(3); , __sl1779))
Have you manually typed those lines or is there a tool that outputs that "preprocessor" ;) output?
 Hence, ref version is taken in account because '__sl1779' is a good
lvalue.
 Troubles seems to come from
 https://github.com/D-**Programming-Language/dmd/blob/**
master/src/expression.c#L4203<https://github.com/D-Programming-Language/d=
md/blob/master/src/expression.c#L4203>

 Thank you very much for finding the related line. :) Quoting:

 Expression *StructLiteralExp::semantic(**Scope *sc)
 { Expression *e;

 // ...

     /* If struct requires a destructor, rewrite as:
      * (S tmp =3D S()),tmp
      * so that the destructor can be hung on tmp.
      */
     if (sd->dtor && sc->func)
     {
         Identifier *idtmp =3D Lexer::uniqueId("__sl");
         VarDeclaration *tmp =3D new VarDeclaration(loc, type, idtmp, new
 ExpInitializer(0, this));
         tmp->storage_class |=3D STCctfe;
         Expression *ae =3D new DeclarationExp(loc, tmp);
         Expression *e =3D new CommaExp(loc, ae, new VarExp(loc, tmp));
         e =3D e->semantic(sc);
         return e;
     }

     return this;
 }

 Is it because destructors cannot be "hung" on literals?

 It is inconsistent that function-returned rvalues do not behave the same
 as literal-rvalues.

 Ali
Apr 08 2013
parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 04/08/2013 07:13 PM, kenji hara wrote:> This is definitely a bug.
 http://d.puremagic.com/issues/show_bug.cgi?id=9907

 Kenji Hara
Thank you! :) You've already fixed it! (Pending merge...) Ali
Apr 08 2013