On Tuesday, 23 January 2024 at 19:27:26 UTC, Renato wrote:
Here's a reduced version of one of the most bizarre bugs I've dealt with in any language. The only reason I didn't move on to another language was because I was too busy at the time.

The code allows for initial values if the index is less than 0, otherwise it returns the element.

```
import std;

double value(T)(T index, double * x) {
  if (index - 5 < 0) {
    return 0.0;
  } else {
    return x[index-5];
  }
}

void main() {
  double[] v = [1.1, 2.2, 3.3];
  // Works
  writeln(value(3, v.ptr));
  // Lucky: program segfaults
  writeln(value(v.length, v.ptr));
}
```

I noticed this behavior only because the program crashes. Once I figured out what was going on, I realized that the thousands of lines of code I had already written needed to be checked and possibly rewritten. If only I had a compiler to do that for me.

This code seems to be doing everything it can to run into undefined behaviour, though?

Why is `index` of a type T that has no requirements at all (when the implementation quite clearly wants `size_t`, or at least an unsigned numerical value)? Why is it using a pointer for x when clearly you intend to use it as a slice? You probably have context that I don't, but I would never expect this sort of code to be anywhere near @safe :D

There are two things things that cause the problem. One is the use of a template and the other is passing an unsigned type. The reason the first parameter uses a template is because there are a lot of types I could send as the first argument, and for some of them there was a transformation of index (for instance, you can pass a date as a long[2], or you can pass another type and pull out the length, that sort of thing). It's using a pointer because I was working with a C library, and that's how the data is stored and passed around.

The data is time series. If after the transformations the index is less than zero, it returns 0.0, which is used for all pre-sample values. If it's non-negative, return the element at that position.

One of the nice things about D is the ability to write this kind of code in such a natural and (I thought) intuitive style. I really like the way all this comes together. There's really no way that code should have been able to do anything wrong. What's terribly frustrating is that the compiler had full knowledge of what was happening, but by choice it didn't say anything, even though D is supposed to prevent these things that happen in C.

Reply via email to