Bug 24527 – opAssign has no effect during CTFE when an array is wrapped in a range

Status
NEW
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2024-04-28T22:03:11Z
Last change time
2024-12-13T19:34:55Z
Assigned to
No Owner
Creator
Jonathan M Davis
Moved to GitHub: dmd#20447 →

Comments

Comment #0 by issues.dlang — 2024-04-28T22:03:11Z
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.
Comment #1 by robert.schadek — 2024-12-13T19:34:55Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/20447 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB