April 28
https://issues.dlang.org/show_bug.cgi?id=24527

          Issue ID: 24527
           Summary: opAssign has no effect during CTFE when an array is
                    wrapped in a range
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P1
         Component: dmd
          Assignee: nobody@puremagic.com
          Reporter: issues.dlang@jmdavisProg.com

This code fails

---
import std.range.primitives;

struct Foo(R)
{
    @property empty()
    {
        return _input.empty;
    }

    @property front()
    {
        return _input.front;
    }

    @property front(ElementType!R val)
    {
        _input.front = val;
    }

    void popFront()
    {
        _input.popFront;
    }

    R _input;
}

auto foo(R)(R r)
{
    return Foo!R(r);
}

unittest
{
    struct S
    {
        int entry;
        void opAssign(S rhs) { this.entry = rhs.entry; }
    }

    auto test()
    {
        auto arr = [S(0),
                    S(1),
                    S(2),
                    S(3),
                    S(4)];

        auto range = arr.foo;
        range.front = S(42);
        return range;
    }

    import std.algorithm.comparison;

    auto runtime = test;
    assert(equal(runtime, [S(42), S(1), S(2), S(3), S(4)]));

    enum ctfe = test;
    // First element is actually S(0), meaning that it wasn't set.
    assert(equal(ctfe, [S(42), S(1), S(2), S(3), S(4)]));
}

void main()
{
}
---

The assignment works just fine if it's done during runtime, but it has no effect if it's done at compile time.

Assigning to the array directly seems to work, so the range wrapper is affecting things somehow.

Similarly, if the opAssign is removed from S, then the assignment works, so it's the combination of using a wrapper struct and defining an opAssign which is triggering this.

Also, whether the range is returned is irrelevant, you can simply assert that the value changed after it was set in test, and it will fail during CTFE but succeed at runtime. I returned the range in the example so that both the compile-time and runtime results could be shown.

If I changed the example so that S is internal to the test function, and add a bool that gets set when test is called, e.g.

---
    auto test()
    {
        bool called;
        struct S
        {
            int entry;
            void opAssign(S rhs) { called = true; this.entry = rhs.entry; }
        }

        auto arr = [S(0),
                    S(1),
                    S(2),
                    S(3),
                    S(4)];

        auto range = arr.foo;
        called = false;
        range.front = S(42);
        assert(called);
        assert(range.front == S(42));
        return range;
    }
---

then it shows that opAssign is called, but it fails to actually set the value. If  I change test to

---
    auto test()
    {
        bool called;
        struct S
        {
            int entry;
            void opAssign(S rhs)
            {
                called = true;
                this.entry = rhs.entry;
                assert(this.entry == 42);
            }
        }

        auto arr = [S(0),
                    S(1),
                    S(2),
                    S(3),
                    S(4)];

        auto range = arr.foo;
        called = false;
        range.front = S(42);
        assert(called);
        assert(range.front == S(42));
        return range;
    }
---

then the assertion inside of opAssign actually passes, but the one checking the value after the call does not. So, it's like opAssign is operating on a copy rather than on the actual value.

--