Thread overview
[Issue 23164] [REG 2.097] Infinite loop on assertion failure + DMD moves struct with copy constructor
Jun 08, 2022
RazvanN
Jul 08, 2022
Walter Bright
Jul 08, 2022
Walter Bright
Jul 08, 2022
Walter Bright
Jul 08, 2022
Mathias LANG
Jul 09, 2022
Iain Buclaw
Dec 22, 2022
Walter Bright
Dec 22, 2022
Iain Buclaw
Feb 05, 2023
Max Samukha
June 08, 2022
https://issues.dlang.org/show_bug.cgi?id=23164

RazvanN <razvan.nitu1305@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           Keywords|                            |backend
                 CC|                            |razvan.nitu1305@gmail.com

--
July 08, 2022
https://issues.dlang.org/show_bug.cgi?id=23164

Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla@digitalmars.com

--- Comment #1 from Walter Bright <bugzilla@digitalmars.com> ---
Remember that D does not allow internal pointers to members.

--
July 08, 2022
https://issues.dlang.org/show_bug.cgi?id=23164

--- Comment #2 from Walter Bright <bugzilla@digitalmars.com> ---
If I add @safe and compile with -preview=dip1000, I get:

test23164.d(15): Error: address of variable `this` assigned to `this` with
longer lifetime
test23164.d(22): Error: address of variable `this` assigned to `this` with
longer lifetime

Line 15 is: this.ptr = &this; in the constructor

Line 22 is: this.ptr = &this; in the copy constructor

--
July 08, 2022
https://issues.dlang.org/show_bug.cgi?id=23164

--- Comment #3 from Walter Bright <bugzilla@digitalmars.com> ---
I find the `auto ref` parameter suspicious. What are you expecting to happen there? Do you expect it to be passed by ref or by value? It doesn't compile with making it just `ref`, so simply removing `auto ref` produces the same result (infinite loop assert in the destructor).

But, passing it by value means another constructor call, and another destructor call.

I'm not sure what you're expecting to happen here. I suspect the problem is with the `auto ref`. Copy constructors should be passing their rvalue by ref, amirite?

--
July 08, 2022
https://issues.dlang.org/show_bug.cgi?id=23164

--- Comment #4 from Mathias LANG <pro.mathias.lang@gmail.com> ---
> Remember that D does not allow internal pointers to members.

If memory serves me well, Andrei mentioned a few years ago that this position is no longer tenable. Not supporting internal pointers means users cannot interface with `std::string`, which is a huge blow too C++ interop. And internal pointers are one of the reasons we got the copy constructors, aren't they ?

> I find the `auto ref` parameter suspicious. What are you expecting to happen there? Do you expect it to be passed by ref or by value?

I expect a single constructor call for a value constructed in the caller.
In other word, I expect:
```
    tmp = std_string(42);
```
and:
```
    auto someLValue = std_string(42);
    tmp = someLValue;
```

to only call `std_string` constructor once.
If I don't use `auto ref` (pass by value), the code will compile for both
snippets above but in the case of `someLValue`, it will call the constructor
one more time.
If I use plain `ref`, the code will not compile for the first snipped (passing
a rvalue).

Note that the original test case was using `in`, which is just `ref` accepting rvalue, so I doubt `auto ref` is at fault here (especially considering LDC and GDC don't have the bug, only DMD does).

--
July 09, 2022
https://issues.dlang.org/show_bug.cgi?id=23164

Iain Buclaw <ibuclaw@gdcproject.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ibuclaw@gdcproject.org

--- Comment #5 from Iain Buclaw <ibuclaw@gdcproject.org> ---
(In reply to Mathias LANG from comment #0)
> The following code shows 2 assertions failures in v2.096.1, an infinite loop
> on v2.097.0:
> ```
--snip--
>     ~this ()
>     {
>         assert(this.ptr is &this);
>     }
--snip--
> ```
> 
> The original test was to see whether or not DMD would move a struct with a copy constructor. The above code compiles and runs fine with LDC & GDC, but asserts with DMD.
To explain GDC behaviour. *Because* there is a destructor (or postblit, copy constructor, or anything that would other make the struct non-trivially copyable) then the type is *always* *implicitly* passed and returned by invisible reference.

The right reduction would be this:
```
struct std_string
{
    std_string* ptr;
    ulong[3] data;

    this (ulong data)
    {
        this.data[] = data;
        this.ptr = &this;
    }

    ~this ()
    {
    }

    // GDC and LDC forces 'auto ref' to be 'ref', even when the
    // front-end decides to drop the 'ref' from the signature.
    ref std_string opAssign()(const auto ref std_string rhs)
    {
        assert(rhs.ptr is &rhs);
        return this;
    }
}

void main ()
{
    std_string tmp;
    tmp = std_string(42);
}
```

--
December 22, 2022
https://issues.dlang.org/show_bug.cgi?id=23164

--- Comment #6 from Walter Bright <bugzilla@digitalmars.com> ---
(In reply to Iain Buclaw from comment #5)
>     // GDC and LDC forces 'auto ref' to be 'ref', even when the
>     // front-end decides to drop the 'ref' from the signature.
>     ref std_string opAssign()(const auto ref std_string rhs)

Do you mean it does this for all functions, or just for opAssign? That regardless of `ref` or `auto ref` or ``, it passes by `ref`?

--
December 22, 2022
https://issues.dlang.org/show_bug.cgi?id=23164

--- Comment #7 from Iain Buclaw <ibuclaw@gdcproject.org> ---
(In reply to Walter Bright from comment #6)
> (In reply to Iain Buclaw from comment #5)
> >     // GDC and LDC forces 'auto ref' to be 'ref', even when the
> >     // front-end decides to drop the 'ref' from the signature.
> >     ref std_string opAssign()(const auto ref std_string rhs)
> 
> Do you mean it does this for all functions, or just for opAssign? That regardless of `ref` or `auto ref` or ``, it passes by `ref`?
Not functions, types.

Either you let the optimizer/backend copy around, make temporaries, or put pieces of an object in registers if it so requires.  Or you tag it with a flag that disallows all the above.

In such cases the only meaningful semantic you can apply to such types is to force all objects to be fully addressable, so passing and returning in memory only.

What kind of objects get this flag? Any type that has non-trivial copy or
destruction semantics - i.e: `~this()`, `this(this)`, and `this(ref const
this)`

--
February 05, 2023
https://issues.dlang.org/show_bug.cgi?id=23164

Max Samukha <maxsamukha@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |maxsamukha@gmail.com

--