www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Unexpected range assignment behaviour

reply Lewis <musicaljelly gmail.com> writes:
```
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.
Jul 19
next sibling parent "H. S. Teoh" <hsteoh qfbox.info> writes:
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?
Jul 19
prev sibling next sibling parent reply Dennis <dkorpel gmail.com> writes:
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.
Jul 19
parent reply matheus <matheus gmail.com> writes:
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.
Jul 19
parent reply Dennis <dkorpel gmail.com> writes:
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.
Jul 19
next sibling parent "H. S. Teoh" <hsteoh qfbox.info> writes:
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
Jul 19
prev sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Jul 19
prev sibling parent Lance Bachmeier <no spam.net> writes:
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.)
Jul 19