Re: Providing implicit conversion of - memory-safety
On Tuesday, 23 January 2024 at 23:40:55 UTC, Danilo wrote: How did you make it correct? Write 2 different versions for `signed` and `unsigned` types? Or could you utilize `core.checkedint` somehow for checking overflow? ```d double value(T)(T index, double * x) { bool overflow; subu(index, 5, overflow); if (overflow) { return 0.0; } else { return x[index-5]; } } ``` This is probably only correct for `unsigned` types. When you have a variable with a "potentially" unsigned type, you must not subtract from it unless you're sure the result is not going negative. The fixed code only subtracts 5 from `index` after checking that `index >= 5`, so it is always safe. Your previous code was trying to do the same thing incorrectly because it just subtracted 5 **first**. This is analogous to checking pointers for null before using them. The type parameter restriction was not necessary, but it was added because the code is assuming that the type can be coerced to size_t, as it's being used as an index - so it's a good idea to make that part of the template's "signature"... even without the type limitation, your code wouldn't compile if this was not the case (but your error message will probably be much worse).
Re: Providing implicit conversion of - memory-safety
On Tuesday, 23 January 2024 at 23:40:55 UTC, Danilo wrote: On Tuesday, 23 January 2024 at 17:54:25 UTC, bachmeier 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. How did you make it correct? The fix is very easy once you realize what's going on. index is ulong, so index - 5 is ulong (even though it doesn't make any sense). All you have to do is change index to index.to!long and the problem is solved.
Re: Providing implicit conversion of - memory-safety
On Tuesday, 23 January 2024 at 21:40:46 UTC, Renato wrote: While I can understand your frustration, it seems to me D is not to blame in this instance because the code is quite patently using unsafe constructs (D does not claim to be fully safe). It pretends to be safe. Consider this: ``` void main() { long y = int.max + 1; writeln(y); // -2147483648 long y2 = int.max; writeln(y2 + 1); // 2147483648 int y3 = y; // Won't compile } ``` It can only be described as a mess of inconsistency. `int y3 = y;` should be an error and it is. `int.max + 1` silently turning into a negative value is frankly insane because it's the same problem that a few lines below won't compile. Would something like this work? ```d double value(T)(T index, double* x) if (is(T : size_t)) ``` There's no way to add a template constraint. Many different types, most of which I defined myself, could be sent as an argument. that it's almost always a mistake to subract from any unsigned type - D scanner correctly warns about that). It's the inconsistency that's the problem. You have to program as if the compiler doesn't catch anything - sometimes it throws errors, sometimes it lets stuff through because maybe that's what you want. `int y3 = y` in the code above is not necessarily an error.
Re: Providing implicit conversion of - memory-safety
On Tuesday, 23 January 2024 at 17:54:25 UTC, bachmeier 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. How did you make it correct? Write 2 different versions for `signed` and `unsigned` types? Or could you utilize `core.checkedint` somehow for checking overflow? ```d double value(T)(T index, double * x) { bool overflow; subu(index, 5, overflow); if (overflow) { return 0.0; } else { return x[index-5]; } } ``` This is probably only correct for `unsigned` types.
Re: Constructing arrays of structs
On Tuesday, 23 January 2024 at 18:15:29 UTC, Stephen Tashiro wrote: If the constructor of a class needs to create an array of structs whose dimensions are inputs, what's the syntax for doing this? For a non-example, the following program errors in main() because in t.array[][] "index [0] is out of bounds". You need to remove `auto` from `auto array = new Point[][](the_dimension,the_dimension);` because it creates a new variable.
Re: Providing implicit conversion of - memory-safety
On Tuesday, 23 January 2024 at 21:40:46 UTC, Renato wrote: While I can understand your frustration, it seems to me D is not to blame in this instance because the code is quite patently using unsafe constructs I wouldn't blame bachmeier, because many reduced testcases distilled from the real code tend to look nonsensical. The arithmetic overflows, silent undesirable signed/unsigned casts and other pitfalls happen in the `@safe` code too. The use of pointers and other unsafe constructs in the provided testcase is a red herring.
Re: Providing implicit conversion of - memory-safety
On Tuesday, 23 January 2024 at 21:18:53 UTC, bachmeier wrote: 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. While I can understand your frustration, it seems to me D is not to blame in this instance because the code is quite patently using unsafe constructs (D does not claim to be fully safe). Would something like this work? ```d double value(T)(T index, double* x) if (is(T : size_t)) { if (index < 5 || x == null) { return 0.0; } else { return x[index - 5]; } } void main() { import std.stdio; import std.range : iota; double[] ds = [1, 2, 3, 4, 5, 6]; ubyte b = 1; foreach (_; iota(12)) { writeln(value(b++, ds.ptr)); } } ``` This will still read rubbish if the index goes past the actual array (because I assume you can't get the exact length from the C code? If you can, you should pass that in and do the bounds check yourself) but there's no unsigned type mistakes (notice that it's almost always a mistake to subract from any unsigned type - D scanner correctly warns about that).
Re: Providing implicit conversion of - memory-safety
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.
Re: Constructing arrays of structs
On Tuesday, 23 January 2024 at 19:32:31 UTC, Stephen Tashiro wrote: Thank you. I don't really understand what the syntax new Point[][](the_dimension,the_dimension); denotes. Does it represent a function? To look up this topic, what are the proper keywords? By experimentation, I found that "new Point[the_dimension][the_dimension];" doesn't compile. This is how you create a _multidimensional array_ in D that's allocated on the heap. The wiki mentions this: https://wiki.dlang.org/Dense_multidimensional_arrays You could also create a "static array" (on the stack, not heap): ```d import std.stdio; void main() { // allocate on the heap int[][] matrix = new int[][](5, 2); writeln(matrix); // allocate on the stack (I don't actually know why the dimensions are reversed! int[2][5] matrix2; writeln(matrix2); } ``` This prints `[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]` twice as they're the same matrix. I normally look at the D website, under the header `Language Reference`, which links to https://dlang.org/spec/spec.html If you're looking for just basics, the D Tour is much more friendly though, click on the `Learn` header: https://tour.dlang.org/ And then try to find what you want either in `D's Basics` or `D's Gems` (or the other headers which are specific to other topics)... these pages normally have links to more in-depth material, so it's always a good starting point. I you're looking for standard library help, then instead of clickin on `Language Reference` on the D's landing page, click on `Library Reference` instead. Almost all stdlib is either under `std` or `core`.
Re: Constructing arrays of structs
On Tuesday, January 23, 2024 12:32:31 PM MST Stephen Tashiro via Digitalmars- d-learn wrote: > On Tuesday, 23 January 2024 at 18:23:22 UTC, Renato wrote: > > This works , your mistake was to not actually assign the array > > to the class' field! > > > > Change this line: > > > > ```d > > auto array = new Point[][](the_dimension,the_dimension); > > ``` > > > > To this: > > > > ```d > > this.array = new Point[][](the_dimension,the_dimension); > > ``` > > Thank you. > > I don't really understand what the syntax > > new Point[][](the_dimension,the_dimension); > > denotes. Does it represent a function? To look up this topic, > what are the proper keywords? > > By experimentation, I found that "new > Point[the_dimension][the_dimension];" doesn't compile. Except for the first dimension, all subsequent dimensions have to go in the parens, since putting them between the brackets indicates that that dimension is for a static array rather than a dynamic array, so it would change the type of the array (allowing for dynamic arrays of static arrays). As it is, you can probably only put the first dimension between the brackets, because other languages do that, and allowing it makes it easier to port code. Arguably though, for consistency, you should always put the dimensions between the parens when allocating a new dynamic array. - Jonathan M Davis
Re: Constructing arrays of structs
On Tuesday, 23 January 2024 at 18:23:22 UTC, Renato wrote: This works , your mistake was to not actually assign the array to the class' field! Change this line: ```d auto array = new Point[][](the_dimension,the_dimension); ``` To this: ```d this.array = new Point[][](the_dimension,the_dimension); ``` Thank you. I don't really understand what the syntax new Point[][](the_dimension,the_dimension); denotes. Does it represent a function? To look up this topic, what are the proper keywords? By experimentation, I found that "new Point[the_dimension][the_dimension];" doesn't compile.
Re: Providing implicit conversion of - memory-safety
On Tuesday, 23 January 2024 at 17:54:25 UTC, bachmeier wrote: On Tuesday, 23 January 2024 at 12:34:38 UTC, Nick Treleaven wrote: But I'm strongly in favour of catching any bugs at compile-time (and have been since before I discovered D). I just object to anyone trying to downgrade the importance of automated memory-safety checking. I'm not downgrading the importance of memory safety. All I'm saying is that you can't sell D as a safe language if has bugs like this. 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
Re: Constructing arrays of structs
On Tuesday, 23 January 2024 at 18:15:29 UTC, Stephen Tashiro wrote: If the constructor of a class needs to create an array of structs whose dimensions are inputs, what's the syntax for doing this? For a non-example, the following program errors in main() because in t.array[][] "index [0] is out of bounds". import std.stdio; struct Point { uint x; uint y; void printInfo() { printf("(%d $d )",x,y); } } class testClass { uint dimension; Point[][] array; this(uint the_dimension) { dimension = the_dimension; auto array = new Point[][](the_dimension,the_dimension); for(uint i = 0; i < dimension; i++) { for(uint j = 0; j < dimension; j++) { array[i][j].x = i; array[i][j].y = j; } } } } void main() { auto t = new testClass(5); for(uint i = 0; i < t.dimension; i++) { for(uint j = 0; j < t.dimension; j++) { printf("(%d %d)",t.array[i][j].x, t.array[i][j].y); //t.array[i][j].printInfo(); } } } This works , your mistake was to not actually assign the array to the class' field! Change this line: ```d auto array = new Point[][](the_dimension,the_dimension); ``` To this: ```d this.array = new Point[][](the_dimension,the_dimension); ```
Constructing arrays of structs
If the constructor of a class needs to create an array of structs whose dimensions are inputs, what's the syntax for doing this? For a non-example, the following program errors in main() because in t.array[][] "index [0] is out of bounds". import std.stdio; struct Point { uint x; uint y; void printInfo() { printf("(%d $d )",x,y); } } class testClass { uint dimension; Point[][] array; this(uint the_dimension) { dimension = the_dimension; auto array = new Point[][](the_dimension,the_dimension); for(uint i = 0; i < dimension; i++) { for(uint j = 0; j < dimension; j++) { array[i][j].x = i; array[i][j].y = j; } } } } void main() { auto t = new testClass(5); for(uint i = 0; i < t.dimension; i++) { for(uint j = 0; j < t.dimension; j++) { printf("(%d %d)",t.array[i][j].x, t.array[i][j].y); //t.array[i][j].printInfo(); } } }
Re: Providing implicit conversion of - memory-safety
On Tuesday, 23 January 2024 at 12:34:38 UTC, Nick Treleaven wrote: But I'm strongly in favour of catching any bugs at compile-time (and have been since before I discovered D). I just object to anyone trying to downgrade the importance of automated memory-safety checking. I'm not downgrading the importance of memory safety. All I'm saying is that you can't sell D as a safe language if has bugs like this. 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.
Re: opApply + const
On Tuesday, 23 January 2024 at 17:22:25 UTC, Paul Backus wrote: On Tuesday, 23 January 2024 at 16:11:25 UTC, ryuukk_ wrote: It works fine.. but when the variable becomes ``const(Stuff)* stuff;`` It gives me: ``` onlineapp.d(13): Error: cannot uniquely infer `foreach` argument types ``` I have no idea what i should be doing, does anyone have a clue? https://issues.dlang.org/show_bug.cgi?id=24353 Thanks, the error message was confusing me.. I tried to fix it https://github.com/dlang/dmd/pull/16086
Re: opApply + const
On Tuesday, 23 January 2024 at 17:07:18 UTC, Alexandru Ermicioi wrote: On Tuesday, 23 January 2024 at 16:11:25 UTC, ryuukk_ wrote: Hello, I have the following: ```D struct Stuff { int opApply(scope int delegate(Stuff*) dg) { return 0; } }; void main() { Stuff* stuff; foreach(it; *stuff) {} } ``` It works fine.. but when the variable becomes ``const(Stuff)* stuff;`` It gives me: ``` onlineapp.d(13): Error: cannot uniquely infer `foreach` argument types ``` I have no idea what i should be doing, does anyone have a clue? Try making opApply const. Thanks that fixed it
Re: opApply + const
On Tuesday, 23 January 2024 at 16:11:25 UTC, ryuukk_ wrote: It works fine.. but when the variable becomes ``const(Stuff)* stuff;`` It gives me: ``` onlineapp.d(13): Error: cannot uniquely infer `foreach` argument types ``` I have no idea what i should be doing, does anyone have a clue? https://issues.dlang.org/show_bug.cgi?id=24353
Re: opApply + const
On Tuesday, 23 January 2024 at 16:11:25 UTC, ryuukk_ wrote: Hello, I have the following: ```D struct Stuff { int opApply(scope int delegate(Stuff*) dg) { return 0; } }; void main() { Stuff* stuff; foreach(it; *stuff) {} } ``` It works fine.. but when the variable becomes ``const(Stuff)* stuff;`` It gives me: ``` onlineapp.d(13): Error: cannot uniquely infer `foreach` argument types ``` I have no idea what i should be doing, does anyone have a clue? Try making opApply const.
opApply + const
Hello, I have the following: ```D struct Stuff { int opApply(scope int delegate(Stuff*) dg) { return 0; } }; void main() { Stuff* stuff; foreach(it; *stuff) {} } ``` It works fine.. but when the variable becomes ``const(Stuff)* stuff;`` It gives me: ``` onlineapp.d(13): Error: cannot uniquely infer `foreach` argument types ``` I have no idea what i should be doing, does anyone have a clue?
Re: Providing implicit conversion of
On Monday, 22 January 2024 at 19:49:19 UTC, Siarhei Siamashka wrote: The two's complement wraparound behavior mandated by the D language spec is a non-technical political decision, intended to make life easier for the DMD compiler developers, but ignoring the needs of the users. Actually it is for compatibility when porting C code to D.
Re: Providing implicit conversion of - memory-safety
On Monday, 22 January 2024 at 19:11:50 UTC, Siarhei Siamashka wrote: On Monday, 22 January 2024 at 16:39:10 UTC, Nick Treleaven wrote: Memory safety issues are a worse class of bug than arithmetic bugs. The latter are reproducible if you feed them the same input. Memory safety bugs are reproducible with the tools like `valgrind`. Not necessarily, valgrind can execute programs too slowly for human input, so anything that relies on timing is difficult to reproduce. It also uses far more memory, it could be too much memory for the system. Whereas arithmetic overflow bugs are a real PITA to debug. Assuming that the incorrect results are even noticed. You're talking about debugging, whereas I'm saying you often don't even have a chance to *notice* memory-safety bugs, because they might not even occur on the development system, only on the production system. And even if you know there's a memory-safety problem, you can't easily narrow down where it is (without language support for memory-safety). With arithmetic problems it's far easier to narrow down which code is causing them. But I'm strongly in favour of catching any bugs at compile-time (and have been since before I discovered D). I just object to anyone trying to downgrade the importance of automated memory-safety checking.