Thread overview
Presence of struct destructor makes lvalue?
Apr 08, 2013
Ali Çehreli
Apr 08, 2013
Maxim Fomin
Apr 08, 2013
Ali Çehreli
Apr 09, 2013
kenji hara
Apr 09, 2013
Ali Çehreli
April 08, 2013
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
April 08, 2013
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
April 08, 2013
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/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 = 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

April 09, 2013
This is definitely a bug.

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

Kenji Hara

2013/4/9 Ali Çehreli <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 = 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/src/expression.c#L4203<https://github.com/D-Programming-Language/dmd/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 = 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
>
>


April 09, 2013
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