Search
Did the implicit conversion from special slice expression to static array ever work?
Sep 22, 2022
Quirin Schroll
Sep 22, 2022
Nick Treleaven
Sep 22, 2022
Nick Treleaven
Sep 22, 2022
Quirin Schroll
Sep 22, 2022
Nick Treleaven
Sep 23, 2022
Nick Treleaven
Sep 24, 2022
Nick Treleaven

In the Expression § Slice Conversion to Static Array, at the end of the section, it says:

>

Certain other forms of slice expression can be implicitly converted to a static array when the slice length can be known at compile-time.

And you find this table (assuming `e` is an expression that contains no side effects and `a` and `b` are compile-time known integers):

Form The length calculated at compile time
`arr[]` `arr.length` if known at compile-time
`arr[a .. b]` `b - a`
`arr[e-a .. e]` `a`
`arr[e .. e+b]` `b`
`arr[e-a .. e+b]` `a + b`
`arr[e+a .. e+b]` `b - a` if `a``b`
`arr[e-a .. e-b]` `a - b` if `a``b`

Has it ever worked?

Expanding on the code example

``````int[] da = [1, 2, 3];
int i = da[0]; // runtime variable

int[2] sa2 = da[i .. i + 2];
assert(sa2 == [2, 3]);

// my tests
int[1] sa1 = da[i .. i + 2]; // core.exception.RangeError
int[3] sa3 = da[i .. i + 2]; // core.exception.RangeError
``````

It seems like there is not attempt made to check the lengths at compile-time. The specification in a sense promised me a compile error here.

On the other hand, I tried with this code:

``````void f(T, size_t n)(T[n] array) { }

void main()
{
int[] xs = [1,2,3,4,5,6];
size_t i = 2;
f(xs[i .. i+3]); // compile error
}
``````

It did not work with any compiler supported by run.dlang.io (LDC and any DMD since 2.064, including beta and nightly).

If it does work in other contexts, either this feature should be removed (it does not work consistently) or be improved so that it lives up to its promise. If nothing like that is implemented, the section should be removed from the spec.

On Thursday, 22 September 2022 at 10:50:28 UTC, Quirin Schroll wrote:

>

Has it ever worked?

Expanding on the code example

``````int[] da = [1, 2, 3];
int i = da[0]; // runtime variable

int[2] sa2 = da[i .. i + 2];
assert(sa2 == [2, 3]);

// my tests
int[1] sa1 = da[i .. i + 2]; // core.exception.RangeError
int[3] sa3 = da[i .. i + 2]; // core.exception.RangeError
``````

It seems like there is not attempt made to check the lengths at compile-time. The specification in a sense promised me a compile error here.

The problem is that you can initialize a static array from (or assign to) a slice, even when the slice length is not statically known. I started a thread on that:
https://forum.dlang.org/post/kqolsorsdmlshxhdqbpq@forum.dlang.org

I did then update the docs to document that:
https://dlang.org/spec/arrays.html#assignment

I think this is surprising because a slice does not implicitly convert to a static array in general, e.g. when passing one to a static array function parameter.

>

On the other hand, I tried with this code:

``````void f(T, size_t n)(T[n] array) { }

void main()
{
int[] xs = [1,2,3,4,5,6];
size_t i = 2;
f(xs[i .. i+3]); // compile error
}
``````

It did not work with any compiler supported by run.dlang.io (LDC and any DMD since 2.064, including beta and nightly).

If you make `i` const, it does work, but it has to be initialized from a compile-time expression too. It should work with a runtime initializer so long as `i` is const.

>

If it does work in other contexts, either this feature should be removed (it does not work consistently) or be improved so that it lives up to its promise. If nothing like that is implemented, the section should be removed from the spec.

Aside from breakage, assignment from a slice of unknown compile-time length could be illegal - the user could easily write `arr[] = slice` instead. That makes it clear that slice copying is happening, which has a runtime check for matching lengths.

For initialization, statically-known slice length could be required. Failing that, at least requiring `slice[]` on an lvalue initializer would make it clear it is initialization from a slice.

On Thursday, 22 September 2022 at 12:48:18 UTC, Nick Treleaven wrote:

>

On Thursday, 22 September 2022 at 10:50:28 UTC, Quirin Schroll wrote:

>
``````int[] da = [1, 2, 3];
int i = da[0]; // runtime variable

int[2] sa2 = da[i .. i + 2];
assert(sa2 == [2, 3]);

// my tests
int[1] sa1 = da[i .. i + 2]; // core.exception.RangeError
int[3] sa3 = da[i .. i + 2]; // core.exception.RangeError
``````

The problem is that you can initialize a static array from (or assign to) a slice, even when the slice length is not statically known.

Or rather cannot be statically known. I.e. `int[1] = da;` is allowed.

On Thursday, 22 September 2022 at 12:48:18 UTC, Nick Treleaven wrote:

>

On Thursday, 22 September 2022 at 10:50:28 UTC, Quirin Schroll wrote:

>

Has it ever worked?

Expanding on the code example

``````int[] da = [1, 2, 3];
int i = da[0]; // runtime variable

int[2] sa2 = da[i .. i + 2];
assert(sa2 == [2, 3]);

// my tests
int[1] sa1 = da[i .. i + 2]; // core.exception.RangeError
int[3] sa3 = da[i .. i + 2]; // core.exception.RangeError
``````

It seems like there is not attempt made to check the lengths at compile-time. The specification in a sense promised me a compile error here.

The problem is that you can initialize a static array from (or assign to) a slice, even when the slice length is not statically known. I started a thread on that:
https://forum.dlang.org/post/kqolsorsdmlshxhdqbpq@forum.dlang.org

I think this is the reason the assignment compiles anyways. This is however not the reason why there is no compile error pointing out a length mismatch. The spec says that both lengths are known at compile-time; thus, if equal, good, if not, it’s an error:

``````int i = 1;
int[3] xs = [ 0, i, i+1, i+3 ]; // compile-error (length mismatch)
``````

The literal is not a constant (it includes run-time values). However, the length is statically known and mismatches.

>

I did then update the docs to document that:
https://dlang.org/spec/arrays.html#assignment

I think this is surprising because a slice does not implicitly convert to a static array in general, e.g. when passing one to a static array function parameter.

>

On the other hand, I tried with this code:

``````void f(T, size_t n)(T[n] array) { }

void main()
{
int[] xs = [1,2,3,4,5,6];
size_t i = 2;
f(xs[i .. i+3]); // compile error
}
``````

It did not work with any compiler supported by run.dlang.io (LDC and any DMD since 2.064, including beta and nightly).

If you make `i` const, it does work, but it has to be initialized from a compile-time expression too. It should work with a runtime initializer so long as `i` is const.

This misses the point entirely. The spec is about run-time expressions. It is very clear about that implicitly using the phrase “no side effects”. Reading a compile-time constant is a very special case of no side effects.

Effectively, the idea is that there’s a run-time value involved, but it serves only as the offset and does not change the length of the segment being sliced (because the values cancel). The whole thing would be trivial if syntax existed for it, e.g. `slice[i: 3]` for `slice[i..i+3]` where `i` would only be evaluated once. We could simply say: If the length is a compile-time constant, there it is.

> >

If it does work in other contexts, either this feature should be removed (it does not work consistently) or be improved so that it lives up to its promise. If nothing like that is implemented, the section should be removed from the spec.

Aside from breakage, assignment from a slice of unknown compile-time length could be illegal - the user could easily write `arr[] = slice` instead.

That makes it clear that slice copying is happening, which has a runtime check for matching lengths.

For initialization, statically-known slice length could be required. Failing that, at least requiring `slice[]` on an lvalue initializer would make it clear it is initialization from a slice.

+1 to that.

On Thursday, 22 September 2022 at 14:31:46 UTC, Quirin Schroll wrote:

>

On Thursday, 22 September 2022 at 12:48:18 UTC, Nick Treleaven wrote:

>

If you make `i` const, it does work, but it has to be initialized from a compile-time expression too. It should work with a runtime initializer so long as `i` is const.

This misses the point entirely. The spec is about run-time expressions. It is very clear about that implicitly using the phrase “no side effects”. Reading a compile-time constant is a very special case of no side effects.

Well I said a const runtime value should work (not just a compile-time constant as works now). const there would make it easy for the compiler to tell there are no side effects. I think you are right that a mutable variable simple lvalue should work as well. Some expressions do have side effects and to ensure every kind of non-side effect expression might require an optimization pass.

I found this is in bugzilla where `m_data[offset .. offset + 14];` is not recognised:
https://issues.dlang.org/show_bug.cgi?id=9165#c1

On Thursday, 22 September 2022 at 10:50:28 UTC, Quirin Schroll wrote:

>

Has it ever worked?

It looks like the docs were merged but the compiler pull was not merged:
https://github.com/dlang/dmd/pull/4209#issuecomment-74065882

Before I found that I implemented `arr[e .. e+b]` for a simple variable `e`:
https://github.com/ntrel/dmd/commits/slice-size

I think at least that should be supported, probably the most common of all the non-constant index forms. But the original pull works for lots of other side-effect free expressions, besides the other forms.

On Friday, 23 September 2022 at 13:35:31 UTC, Nick Treleaven wrote:

>

It looks like the docs were merged but the compiler pull was not merged:
https://github.com/dlang/dmd/pull/4209#issuecomment-74065882

Commented out those docs & fixed other examples:
https://github.com/dlang/dlang.org/pull/3416