Bug 20964 – poor CTFE support for backward pointer iteration

Status
NEW
Severity
blocker
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2020-06-21T03:12:07Z
Last change time
2024-12-13T19:09:34Z
Keywords
CTFE, pull, rejects-valid
Assigned to
No Owner
Creator
Илья Ярошенко
Moved to GitHub: dmd#19730 →

Comments

Comment #0 by ilyayaroshenko — 2020-06-21T03:12:07Z
Pointer can refer to the next right element after memory block. However, referring to the next left element before the memory block is prohibited. ``` uint foo(uint[3] m) { auto p = m.ptr; p += 3; // ok, p refers to the next element after block! p -= 4; // fails: `cannot assign pointer to index -1 inside memory block [0..3]` p++; return *p; } static assert(foo([3, 2, 4]) == 3); ``` The use case: uint* retroStart; // refers to the last element uint length; foreach (i; 0 .. length) { // use *retroStart retroStart--; } This case is a reduced use case for generic retro iterators of Mir. Ranges can't be used for this case.
Comment #1 by dlang-bot — 2021-09-22T04:08:11Z
@9il created dlang/dmd pull request #13094 "Fix Issue 20964" fixing this issue: - Fix Issue 20964 https://github.com/dlang/dmd/pull/13094
Comment #2 by bugzilla — 2021-09-22T05:22:37Z
It is not normal practice nor allowed by the memory model to point before the beginning of a memory block. Pointing one past the end is allowed. The conventional solution is to use pre-decrement for reversing, that way the iteration can start with a pointer one past the end. I'm not understanding why the conventional solution won't work for your case.
Comment #3 by bugzilla — 2021-09-22T05:25:43Z
To elaborate, decrementing past the beginning could lead to a wraparound, which is why this restriction is in the memory model.
Comment #4 by ilyayaroshenko — 2021-09-22T06:40:27Z
(In reply to Walter Bright from comment #2) > It is not normal practice nor allowed by the memory model to point before > the beginning of a memory block. Pointing one past the end is allowed. > > The conventional solution is to use pre-decrement for reversing, that way > the iteration can start with a pointer one past the end. I am not sure what do you mean by memory model. If you mean a GC model, than it is fine to free the memory as soon as the point moved out of the left bound. At least that works well for Mir. > I'm not understanding why the conventional solution won't work for your case. It may work. It is just annoying to rework it each time. We have a code that works in runtime and we expect it works at compile time, however it doesn't.
Comment #5 by ilyayaroshenko — 2021-09-22T06:50:33Z
(In reply to Илья Ярошенко from comment #4) > (In reply to Walter Bright from comment #2) > > It is not normal practice nor allowed by the memory model to point before > > the beginning of a memory block. Pointing one past the end is allowed. > > > > The conventional solution is to use pre-decrement for reversing, that way > > the iteration can start with a pointer one past the end. > > I am not sure what do you mean by memory model. If you mean a GC model, than > it is fine to free the memory as soon as the point moved out of the left > bound. At least that works well for Mir. > > > I'm not understanding why the conventional solution won't work for your case. > > It may work. It is just annoying to rework it each time. We have a code that > works in runtime and we expect it works at compile time, however it doesn't. Ah, no. It won't work because the code is generic. Assume a double retro operation with another modifier between two retros. So, your variant for the second retro will refer to the next after right bound of the last first retro, which is the next left before the left bound of the original array.
Comment #6 by uplink.coder — 2021-09-22T10:06:38Z
To make this code example clearer: this compiles without complaints in non-@safe code and works. void main() { uint[3] arr = [1,2,3]; assert(foo(arr) == arr[0]); } uint foo(uint[3] m) { auto p = &m[0]; // pointing the first element p += 8; // pointing to an invalid address &m[9] p -= 11; // pointer to an invalid address &[m-2] p++; // after this statement pointing the valid address &m[-1] again return *++p; // *p should yield m[0]; } Because at ctfe we can actually check that the final position of the pointer the point of using it to access memory is in the range of the original memory block; I have no issues with allowing this.
Comment #7 by dlang-bot — 2021-09-22T14:25:11Z
@9il created dlang/dmd pull request #13096 "Fix Issue 20964" fixing this issue: - Fix Issue 20964 https://github.com/dlang/dmd/pull/13096
Comment #8 by robert.schadek — 2024-12-13T19:09:34Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19730 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB