Thread overview
Unexpected range assignment behaviour
Jul 19
Lewis
Jul 19
Dennis
Jul 19
matheus
Jul 19
Dennis
July 19
string[3][string] lookup;
string[] dynArray = ["d", "e", "f"];
lookup["test"] = dynArray[0..$];

This fails at runtime with RangeError. But if I change that last line to:

lookup["test"] = dynArray[0..3];

then it works. But the value of $ here is 3. Why do I get a RangeError at runtime even though the slice is the correct size (and the same size as the hardcoded one that works)? I would have expected to only get a RangeError if at runtime the value turned out to be wrong.

July 19
On Fri, Jul 19, 2024 at 09:34:13AM +0000, Lewis via Digitalmars-d-learn wrote:
> ```
> string[3][string] lookup;
> string[] dynArray = ["d", "e", "f"];
> lookup["test"] = dynArray[0..$];
> ```
> 
> This fails at runtime with RangeError. But if I change that last line to:
> 
> ```
> lookup["test"] = dynArray[0..3];
> ```
> 
> then it works. But the value of $ here is 3. Why do I get a RangeError at runtime even though the slice is the correct size (and the same size as the hardcoded one that works)? I would have expected to only get a RangeError if at runtime the value turned out to be wrong.

Sounds like a bug.  First, there's an incompatibility between AA value type and the type being assigned: the value type of `lookup` is a static array, whereas a slice is a dynamic array.  But since the compiler allows this assignment, it should work.  I suspect there's a frontend bug somewhere in how assignments of slices to static arrays are implemented.


T

-- 
MAS = Mana Ada Sistem?
July 19

On Friday, 19 July 2024 at 09:34:13 UTC, Lewis wrote:

>

But the value of $ here is 3. Why do I get a RangeError at runtime even though the slice is the correct size (and the same size as the hardcoded one that works)?

The range 0 .. 3 has compile time known length, so it gets converted to string[3]:

lookup["test"] = dynArray[0 .. 3];
// becomes
lookup["test"] = cast(string[3]) dynArray[0 .. 3];

The key "test" doesn't exist yet, but because it's an assignment, it gets created.
However, 0 .. $ depends on a run-time variable here, so it doesn't convert to a static array and does slice assignment:

lookup["test"] = dynArray[0 .. $];
// becomes
lookup["test"][0 .. $] = dynArray[0 .. $];

Now, you get a range error because "test" doesn't exist in lookup, and slice assignment doesn't create a new entry.

July 19

On Friday, 19 July 2024 at 09:34:13 UTC, Lewis wrote:

>
string[3][string] lookup;
string[] dynArray = ["d", "e", "f"];
lookup["test"] = dynArray[0..$];

This fails at runtime with RangeError. But if I change that last line to:

lookup["test"] = dynArray[0..3];

then it works. But the value of $ here is 3. Why do I get a RangeError at runtime even though the slice is the correct size (and the same size as the hardcoded one that works)? I would have expected to only get a RangeError if at runtime the value turned out to be wrong.

The simplest solution is to keep them consistent:

string[3][string] lookup;
string[3] dynArray = ["d", "e", "f"];

or

string[][string] lookup;
string[] dynArray = ["d", "e", "f"];

Avoid the temptation to mix static and dynamic arrays and your life will be easier. If you run into a situation where it's tough to avoid, use an explicit conversion:

lookup["test"] = dynArray.to!(string[3])[0..$];

(I don't know all the complicated under the hood stuff as others do, but I know what works and why.)

July 19
On Friday, 19 July 2024 at 15:33:34 UTC, Dennis wrote:
> On Friday, 19 July 2024 at 09:34:13 UTC, Lewis wrote:
>> But the value of $ here is 3. Why do I get a RangeError at runtime even though the slice is the correct size (and the same size as the hardcoded one that works)?
>
> The range `0 .. 3` has compile time known length, so it gets converted to string[3]:
>
> ```D
> lookup["test"] = dynArray[0 .. 3];
> // becomes
> lookup["test"] = cast(string[3]) dynArray[0 .. 3];
> ```
>
> The key "test" doesn't exist yet, but because it's an assignment, it gets created.
> However, `0 .. $` depends on a run-time variable here, so it doesn't convert to a static array and does slice assignment:
>
> ```D
> lookup["test"] = dynArray[0 .. $];
> // becomes
> lookup["test"][0 .. $] = dynArray[0 .. $];
> ```
>
> Now, you get a range error because "test" doesn't exist in `lookup`, and slice assignment doesn't create a new entry.

Hi Dennis, I undestood your explanation, and based on that  couldn't this case for example be caught during the compiling time?

Thanks,

Matheus.
July 19

On Friday, 19 July 2024 at 17:20:22 UTC, matheus wrote:

>

couldn't this case for example be caught during the compiling time?

The RangeError is only thrown when at runtime, the key doesn't exist, so that can't be caught. The real problem is implicit slicing of static arrays, which I'm not a fan of, but removing it is a breaking change. So perhaps Associative Arrays should be enhanced to create keys on slice assignment.

July 19
On Fri, Jul 19, 2024 at 05:48:37PM +0000, Dennis via Digitalmars-d-learn wrote:
> On Friday, 19 July 2024 at 17:20:22 UTC, matheus wrote:
> > couldn't this case for example be caught during the compiling time?
> 
> The RangeError is only thrown when at runtime, the key doesn't exist, so that can't be caught. The real problem is implicit slicing of static arrays, which I'm not a fan of, but removing it is a breaking change. So perhaps Associative Arrays should be enhanced to create keys on slice assignment.

IMO, implicit slicing of static arrays ought to be killed with fire. This is not the first time it has caused problems.  In the past it used to cause issues with the implicit slice escaping the scope of the original static array, leading to dangling pointers and subsequent memory corruption / UB.  With -dip1000 the situation has somewhat improved, but not entirely. It still causes nasty surprises when a slice was implicitly taken where it was unexpected.  This case here is another example of the problems that it causes.


T

-- 
I don't trust computers, I've spent too long programming to think that they can get anything right. -- James Miller
July 19
On Friday, July 19, 2024 12:02:55 PM MDT H. S. Teoh via Digitalmars-d-learn wrote:
> On Fri, Jul 19, 2024 at 05:48:37PM +0000, Dennis via Digitalmars-d-learn
wrote:
> > On Friday, 19 July 2024 at 17:20:22 UTC, matheus wrote:
> > > couldn't this case for example be caught during the compiling time?
> >
> > The RangeError is only thrown when at runtime, the key doesn't exist, so that can't be caught. The real problem is implicit slicing of static arrays, which I'm not a fan of, but removing it is a breaking change. So perhaps Associative Arrays should be enhanced to create keys on slice assignment.
>
> IMO, implicit slicing of static arrays ought to be killed with fire. This is not the first time it has caused problems.  In the past it used to cause issues with the implicit slice escaping the scope of the original static array, leading to dangling pointers and subsequent memory corruption / UB.  With -dip1000 the situation has somewhat improved, but not entirely. It still causes nasty surprises when a slice was implicitly taken where it was unexpected.  This case here is another example of the problems that it causes.

Very, very hot fire. :)

IIRC, Atila has indicated that he would like to kill implicit slicing of static arrays (though that's going to require that we have actually started doing Editions first), so we may end up finally getting rid of it. I don't know how much convincing it will take for Walter though.

I actually brought this up with Walter years ago at one of the dconfs in Berlin, suggesting that it was a big @safety mistake, but he preferred the idea of improving the language to catch escaping over removing the implicit slicing (which is probably part of why DIP 1000 and the related changes have unfortunately become a thing).

Regardless of DIP 1000 though, IMHO, the implicit slicing just causes confusion and invisible behavior simply so that you can avoid using an explicit [] - and some of that isn't even related to bugs per se. For instance, plenty of folks end up trying to pass a static array to a range-based function and get confused when that doesn't work, since if the function took a dynamic array, it would work (and that also makes it more problematic to change a function so that it takes a range instead of a dynamic array). It's one of those features that seems like it's a nice usability improvement at first glance but which ultimately is a footgun.

And when you get a more complex example like the one in this thread, it's that much worse, since even if you know enough to suspect that something along those lines might be the problem, I bet that most of us would not immediately come to that conclusion. It's just too subtle. And all to avoid typing a couple of characters.

- Jonathan M Davis