Thread overview
Why this doesn't produce an error or works as expected?
2 days ago
partypooper
2 days ago
monkyyy
2 days ago
partypooper
2 days ago
partypooper
1 day ago
partypooper
2 days ago
struct Vector
{
    float x, y;

    Vector opBinary(string op)(inout Vector rhs) const if (op == "+")
    {
        return Vector(mixin("x", op, "rhs.x"), mixin("y", op, "rhs.y"),);
    }

    ref Vector opOpAssign(string op)(inout Vector rhs) if (op == "+" || op == "-")
    {
        mixin("x", op, "=rhs.x;");
        mixin("y", op, "=rhs.y;");
        return this;
    }
}

struct A
{
    Vector _pos, _dest;

    this(Vector pos)
    {
        this._pos = pos;
        this._dest = pos;
    }

    Vector pos() => _pos;
    Vector pos(Vector v)
    {
        _dest.x = v.x;
        _dest.y = v.y;
        return _pos = v;
    }
}

void main()
{
    import std.stdio : writeln;

    A a = A(Vector(10, 20));
    a.pos += Vector(1, 2); // silently does nothing
    writeln(a);
}

How I can make it to work, if it is possible? Or else how to make compiler error/warning for such cases?

2 days ago

On Friday, 4 July 2025 at 17:15:48 UTC, partypooper wrote:

>
struct Vector
{
    float x, y;

    Vector opBinary(string op)(inout Vector rhs) const if (op == "+")
    {
        return Vector(mixin("x", op, "rhs.x"), mixin("y", op, "rhs.y"),);
    }

    ref Vector opOpAssign(string op)(inout Vector rhs) if (op == "+" || op == "-")
    {
        mixin("x", op, "=rhs.x;");
        mixin("y", op, "=rhs.y;");
        return this;
    }
}

struct A
{
    Vector _pos, _dest;

    this(Vector pos)
    {
        this._pos = pos;
        this._dest = pos;
    }

    Vector pos() => _pos;
    Vector pos(Vector v)
    {
        _dest.x = v.x;
        _dest.y = v.y;
        return _pos = v;
    }
}

void main()
{
    import std.stdio : writeln;

    A a = A(Vector(10, 20));
    a.pos += Vector(1, 2); // silently does nothing
    writeln(a);
}

How I can make it to work, if it is possible? Or else how to make compiler error/warning for such cases?

if you add ref to line 28 it works; there wont be a reasonable way to make an error happen, you just have to know when to do refness

2 days ago

On Friday, 4 July 2025 at 17:34:59 UTC, monkyyy wrote:

>

On Friday, 4 July 2025 at 17:15:48 UTC, partypooper wrote:
if you add ref to line 28 it works; there wont be a reasonable way to make an error happen, you just have to know when to do refness

It doesn't work. Or it works even worse: changing _pos, but not _dest. What is going on is that on += it for some reason invokes "getter", not "setter". I specifically omitted "ref", because I already have known of that behavior.

2 days ago

On Friday, 4 July 2025 at 18:04:55 UTC, partypooper wrote:

>

It doesn't work. Or it works even worse: changing _pos, but not _dest. What is going on is that on += it for some reason invokes "getter", not "setter". I specifically omitted "ref", because I already have known of that behavior.

In other words you through reference of the "getter" which returns "_pos" setting it to the right part of the "+=". Which in ideal world should error too.

You can get what i'm want is to duplicating

_dest.x = _pos.x
_dest.y = _pos.y
``` In a getter, but it is definitely not sound right.


1 day ago
On Friday, July 4, 2025 12:04:55 PM Mountain Daylight Time partypooper via Digitalmars-d-learn wrote:
> On Friday, 4 July 2025 at 17:34:59 UTC, monkyyy wrote:
> > On Friday, 4 July 2025 at 17:15:48 UTC, partypooper wrote:
> > if you add ref to line 28 it works; there wont be a reasonable
> > way to make an error happen, you just have to know when to do
> > refness
>
> It doesn't work. Or it works even worse: changing _pos, but not _dest. What is going on is that on `+=` it for some reason invokes "getter", not "setter". I specifically omitted "ref", because I already have known of that behavior.

The only time that a setter function would be triggered would be assignment such as

    a.pos = foo;

+= is not assignment. Sure, conceptually, you're adding two values together and then assigning the result, but in practice, it's just a function call, and that function call is opOpAssign!"+".

So, in order for the compiler to use += on a property in the way that you're attempting, all it can do is get the value with the getter and then call opOpAssign!"+" on it. In order to use the setter, it would need to break the operation up into + and = so that it could use the getter with + and the setter with =, but that's not how overloaded operators work. Technically, as far as the language is concerned, += is unrelated to + or =. It's its own thing.

The only way that I see that this could work would be if the language
mandated that a += b; was equivalent to a = a + b; _and_ that the compiler
was allowed to turn a += b; into a = a + b; Obviously, if a += b; and
a = a + b; aren't logically equivalent, then the overloaded operator is to
overloading these operators in an appropriate way, but I don't think that
the language actually has any requirement that there's any equivalent, and
it certainly doesn't allow the compiler to transform one into the other.

So, making it so that the compiler is allowed to do such a transformation would require change to the language (presumably by going through the DIP process). I don't know how likely that is, but it may happen, since some folks do want to make it so that property functions are able to do more.

That being said, as things stand, this really should be an error, and if you use built-in types, it is. E.G.

    void main ()
    {
        S s;
        s.prop += 42;
    }

    struct S
    {
        int i;

        int prop() { return i; }
        void prop(int i) { this.i = i; }
    }

will give

q.d(4): Error: cannot modify expression `s.prop()` because it is not an
        lvalue
    s.prop += 42;
    ^

Unfortunately though, the compiler does not have that restriction when opOpAssign is overloaded. There is an open bug report for it, but it doesn't seem to have been a high enough priority for anyone to fix it yet:

https://github.com/dlang/dmd/issues/19061

So, hopefully it gets fixed at some point here, but for now, we're kind of stuck, and you'll need unit tests to catch the cases where you accidentally do some form of assignment on an rvalue when overloaded operators are involved.

- Jonathan M Davis




1 day ago
On Saturday, 5 July 2025 at 08:08:08 UTC, Jonathan M Davis wrote:
> On Friday, July 4, 2025 12:04:55 PM Mountain Daylight Time partypooper via Digitalmars-d-learn wrote:
>> [...]
>
> The only time that a setter function would be triggered would be assignment such as
>
> [...]

Thanks for your thorough answer. Thats for sure a little bit sad. It would be only a little bit of inconvenience if it would error, but silently ignoring such thing could really be one time hard to spot bug.
1 day ago
On Saturday, July 5, 2025 2:19:11 AM Mountain Daylight Time partypooper via Digitalmars-d-learn wrote:
> On Saturday, 5 July 2025 at 08:08:08 UTC, Jonathan M Davis wrote:
> > On Friday, July 4, 2025 12:04:55 PM Mountain Daylight Time partypooper via Digitalmars-d-learn wrote:
> >> [...]
> >
> > The only time that a setter function would be triggered would be assignment such as
> >
> > [...]
>
> Thanks for your thorough answer. Thats for sure a little bit sad. It would be only a little bit of inconvenience if it would error, but silently ignoring such thing could really be one time hard to spot bug.

Well, thorough unit tests can definitely help catch stuff like this, but yeah, it really should be an error. I expect that it's just an oversight that hasn't gotten enough attention and thus hasn't been a priority among all of the various bugs that come up, so it hasn't been fixed yet.

- Jonathan M Davis