https://issues.dlang.org/show_bug.cgi?id=24562

          Issue ID: 24562
           Summary: inout on the copy constructor makes it so that a
                    normal opAssign does not work with multiple layers of
                    objects
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: blocker
          Priority: P1
         Component: dmd
          Assignee: nob...@puremagic.com
          Reporter: issues.dl...@jmdavisprog.com

This code fails to compile

---
void main()
{
    static struct V
    {
        int _data;

        this(ref return scope inout(V) rhs) scope @trusted inout pure nothrow
        {
            this._data = rhs._data;
        }

        ref opAssign(T)(auto ref T rhs)
            if(is(immutable T == immutable V))
        {
            this._data = rhs._data;
            return this;
        }
    }

    static struct S1
    {
        V v;
    }

    S1 a;
    auto b = a;
    a = b;

    static struct S2
    {
        S1 s;
    }

    S2 s;
    auto t = s;
    s = t;
}
---

and gives the error:

---
q.d(36): Error: generated function `q.main.S2.opAssign` cannot be used because
it is annotated with `@disable`
---

No reason is given as to why it's been disabled, so I really don't know what
the problem is, but removing the copy constructor fixes the problem (which
obviously is unacceptable for code that needs a copy constructor), and adding
inout to opAssign fixes the problem. However, putting inout on an opAssign
makes _no_ sense, because outside of really weird edge cases when the type
isn't actually holding the data, you can't assign to a const or immutable
variable. And opAssign with inout on it shouldn't even compile if it assigns to
any members (like this opAssign does), since the object could be const or
immutable underneath the hood. If opAssign isn't templated, then putting inout
on it gives the proper error (I've reported the issue with inout and opAssign
here: https://issues.dlang.org/show_bug.cgi?id=24561).

Regardless, something really weird is happening with opAssign to make it so
that you have to declare it with inout and templatize it to make example code
work.

And note that it's only S2 which has issues. Assigning to an S1 is fine. So, it
seems likely that something funny is going on with the opAssign generated for
S1 which then makes it so that the opAssign for S2 cannot be generated.

Maybe there are actually is a good reason for this situation, and someone can
explain it, but certainly from what I can see, this should be considered a
compiler bug. It should _never_ be required to annotate opAssign with
mutability qualifiers, and in the vast majority of cases, it would make no
sense for the compiler to generate an opAssign with any mutability qualifiers,
since its implementation wouldn't work anyway.

I'm marking this a blocker, because it's blocking stuff that I'm trying to do
at Symmetry. Copy constructors and opAssign need to play together nicely, and
they're clearly not. Part of the problem is that the language basically forces
you to put inout on a copy constructor right now, but even if that problem is
fixed, it should still work to have a copy constructor with inout while
simultaneously having an opAssign with no mutability qualifiers - and it _does_
work for the type itself and the type containing it, but once you go one level
deeper, it doesn't. Instead, it just gives a cryptic error message about
opAssign having been disabled.

--

Reply via email to