www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Shouldn't invalid references like this fail at compile time?

reply Aedt <aedt rocketmail.com> writes:
I was asked in Reddit 
(https://www.reddit.com/r/learnprogramming/comments/7ru82l/i_was_thinking_of_using
d_haxe_or_another/) how would D handle the following similar D code. I'm
surprised that both dmd and ldc provides no warnings even with -w argument
passed.

import std.stdio;

void main()
{
	string foo = "foo";
	string* p1, p2;
	
	string*[] ls;
	ls ~= &foo;
	p1 = ls[0];
	ls.destroy();
	p2 = ls[0];
	writeln(p2);

}
Jan 22
next sibling parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Monday, 22 January 2018 at 23:30:16 UTC, Aedt wrote:
 I was asked in Reddit 
 (https://www.reddit.com/r/learnprogramming/comments/7ru82l/i_was_thinking_of_using
d_haxe_or_another/) how would D handle the following similar D code. I'm
surprised that both dmd and ldc provides no warnings even with -w argument
passed.

 import std.stdio;

 void main()
 {
 	string foo = "foo";
 	string* p1, p2;
 	
 	string*[] ls;
 	ls ~= &foo;
 	p1 = ls[0];
 	ls.destroy();
 	p2 = ls[0];
 	writeln(p2);

 }
D is not memory safe by default (unfortunately), so it's not surprising to me that you can do this in ` system` code. I would be surprised if the compiler allowed you to do something like this in ` safe` code. To make your programs memory safe, you should add ` safe` to your `main` function. Mike
Jan 22
next sibling parent Seb <seb wilzba.ch> writes:
On Tuesday, 23 January 2018 at 00:20:45 UTC, Mike Franklin wrote:
 On Monday, 22 January 2018 at 23:30:16 UTC, Aedt wrote:
 [...]
D is not memory safe by default (unfortunately), so it's not surprising to me that you can do this in ` system` code. I would be surprised if the compiler allowed you to do something like this in ` safe` code. To make your programs memory safe, you should add ` safe` to your `main` function. Mike
Yep, DMD will complain: https://run.dlang.io/is/x0Xfx8
Jan 22
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 01/23/2018 01:20 AM, Mike Franklin wrote:
 I would be surprised if 
 the compiler allowed you to do something like this in ` safe` code.
You might get surprised then, if you expect the compiler to reject code like that statically. If you add ` safe`, the compiler rejects this line: ls ~= &foo; But that line would only be problematic if the pointer would leave the scope of the function. It doesn't, so this is actually safe. But the compiler isn't smart enough to see this. The real question is about this line: p2 = ls[0]; That's an out-of-bounds access, and the compiler does not catch this statically. Instead, it inserts bounds-checking code that crashes the program safely with an `Error`.
Jan 22
next sibling parent Mike Franklin <slavo5150 yahoo.com> writes:
On Tuesday, 23 January 2018 at 01:08:19 UTC, ag0aep6g wrote:

 If you add ` safe`, the compiler rejects this line:

     ls ~= &foo;

 But that line would only be problematic if the pointer would 
 leave the scope of the function. It doesn't, so this is 
 actually safe. But the compiler isn't smart enough to see this.
https://issues.dlang.org/show_bug.cgi?id=18281
Jan 22
prev sibling next sibling parent Mike Franklin <slavo5150 yahoo.com> writes:
On Tuesday, 23 January 2018 at 01:08:19 UTC, ag0aep6g wrote:

 The real question is about this line:

     p2 = ls[0];

 That's an out-of-bounds access, and the compiler does not catch 
 this statically. Instead, it inserts bounds-checking code that 
 crashes the program safely with an `Error`.
In trying to work out a solution to that, I ran across this oddity: https://issues.dlang.org/show_bug.cgi?id=18282
Jan 22
prev sibling parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Tuesday, 23 January 2018 at 01:08:19 UTC, ag0aep6g wrote:

 The real question is about this line:

     p2 = ls[0];

 That's an out-of-bounds access, and the compiler does not catch 
 this statically. Instead, it inserts bounds-checking code that 
 crashes the program safely with an `Error`.
Due to the aforementioned bugs in my prior posts, I couldn't even make an example to demonstrate in safe code, so I modified the example slightly in an effort to reproduce the same problem. import std.stdio; void main() safe { string foo = "foo"; string* ls0; string* p1, p2; ls0 = &foo; p1 = ls0; ls0.destroy(); p2 = ls0; writeln(p2.length); } Error: program killed by signal 11 https://run.dlang.io/is/ecYAKZ Yeah, that's pretty poopy. Not sure how to precisely define the problem here. Should `destroy` be ` system` so it can't be called in ` safe` code, or should the compiler be smart enough to figure out the flow control and throw an error? Mike
Jan 22
next sibling parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Tuesday, 23 January 2018 at 02:25:57 UTC, Mike Franklin wrote:

 Due to the aforementioned bugs in my prior posts, I couldn't 
 even make an example to demonstrate in  safe code, so I 
 modified the example slightly in an effort to reproduce the 
 same problem.

 import std.stdio;

 void main()  safe
 {
     string foo = "foo";
     string* ls0;
     string* p1, p2;

     ls0 = &foo;
     p1 = ls0;
     ls0.destroy();
     p2 = ls0;
     writeln(p2.length);
 }

 Error: program killed by signal 11

 https://run.dlang.io/is/ecYAKZ
Gah!!! I screwed up that example, and I can't edit the post. See the example here: import std.stdio; void main() safe { string foo = "foo"; string* ls0; string* p1, p2; ls0 = &foo; p1 = ls0; ls0.destroy(); p2 = ls0; writeln(p2.length); } Compile with `-dip1000` Error: program killed by signal 11 https://run.dlang.io/is/6L6zcH So that's bad. But it looks like a bug in `-dip1000`, because if I compile without `-dip1000`, I get: onlineapp.d(9): Error: cannot take address of local foo in safe function main https://run.dlang.io/is/rHpuf1 Mike
Jan 22
next sibling parent Mike Franklin <slavo5150 yahoo.com> writes:
On Tuesday, 23 January 2018 at 02:38:42 UTC, Mike Franklin wrote:

 So that's bad.  But it looks like a bug in `-dip1000`
https://issues.dlang.org/show_bug.cgi?id=18283
Jan 22
prev sibling parent ag0aep6g <anonymous example.com> writes:
On 01/23/2018 03:38 AM, Mike Franklin wrote:
 import std.stdio;
 
 void main()  safe
 {
      string foo = "foo";
      string* ls0;
      string* p1, p2;
 
      ls0 = &foo;
      p1 = ls0;
      ls0.destroy();
      p2 = ls0;
      writeln(p2.length);
 }
 
 Compile with `-dip1000`
 
 Error: program killed by signal 11
 
 https://run.dlang.io/is/6L6zcH
 
 So that's bad.  But it looks like a bug in `-dip1000`, because if I 
 compile without `-dip1000`, I get:
 
 onlineapp.d(9): Error: cannot take address of local foo in  safe 
 function main
 
 https://run.dlang.io/is/rHpuf1
No bug. `&foo` never leaves the scope, so `-dip1000` correctly allows it. If you try to `return p1;` or `return p2;`, you get errors from `-dip1000`. Other than that, `ls0.destroy();` just does `ls0 = null;` and then the `writeln` does a null dereference which is considered to be a guaranteed segfault. Segfaults are considered safe and ` safe` is not supposed to prevent them.
Jan 22
prev sibling next sibling parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Tuesday, 23 January 2018 at 02:25:57 UTC, Mike Franklin wrote:
 Not sure how to precisely define the problem here.  Should 
 `destroy` be ` system` so it can't be called in ` safe` code, 
 or should the compiler be smart enough to figure out the flow 
 control and throw an error?

 Mike
The compiler should be taught that any access to a `.destroy()`ed object is invalid i.e. that its lifetime ends when destroy is called.
Jan 22
prev sibling parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Tuesday, 23 January 2018 at 02:25:57 UTC, Mike Franklin wrote:

 Should `destroy` be ` system` so it can't be called in ` safe` 
 code, or should the compiler be smart enough to figure out the 
 flow control and throw an error?
Interestingly, `destroy` is an unsafe operation for classes. import std.stdio; class A { void hello() safe { writeln("hello"); } } void main() safe { A a = new A(); a.hello(); destroy(a); // onlineapp.d(12): Error: safe function 'D main' cannot call // system function 'object.destroy!(A).destroy' a.hello(); } https://run.dlang.io/is/AwKBc3 But it's not an unsafe operation for structs import std.stdio; struct A { int i; void print() safe { writeln(i); } } void main() safe { A* a = new A(); a.print(); // OK a.destroy(); a.print(); // Error! } https://run.dlang.io/is/Fm7qBR Not sure if that's a bug or not. Mike
Jan 22
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/22/18 11:11 PM, Mike Franklin wrote:
 On Tuesday, 23 January 2018 at 02:25:57 UTC, Mike Franklin wrote:
 
 Should `destroy` be ` system` so it can't be called in ` safe` code, 
 or should the compiler be smart enough to figure out the flow control 
 and throw an error?
Interestingly, `destroy` is an unsafe operation for classes.
Because it's calling a system function, rt_finalize. This function calls whatever is in the destructor, and because it works on Object level, it has no idea what the actual attributes of the derived destructor are. This needs to be fixed, but a whole host of issues like this exist with Object.
 But it's not an unsafe operation for structs
Because struct destructors are not virtual. The compiler can tell when a struct destructor is unsafe: https://run.dlang.io/is/o3ujrP Note, I had to call destroy in a sub-function because if I made main safe, it would fail to compile due to automatic destruction.
 
 Not sure if that's a bug or not.
Not a bug. Also, as others have pointed out, null dereferences are also considered safe [1]. destroying an object doesn't actually deallocate it. It puts it into a state that is safe to call, but will likely crash. On 1/22/18 9:43 PM, Nicholas Wilson wrote:
 The compiler should be taught that any access to a `.destroy()`ed object
 is invalid i.e. that its lifetime ends when destroy is called.
destroy is just a function, there shouldn't be any special magic for it (we have enough of that already). And in fact its lifetime has not ended, it's just destructed, and left as an empty shell. The idea behind destroy is to decouple destruction from deallocation (as delete combines the two). safe is all about memory safety, nothing else. As long as you can't corrupt memory, it is safe. -Steve [1] Note: the reason they are safe is because they generally result in a segfault, which doesn't harm any memory. This is very much a user-space POV, and doesn't take into account kernel-space where null dereferences may actually be valid memory! It also doesn't (currently) take into account possible huge objects that could extend into valid memory space, even in user space.
Jan 23
next sibling parent Mike Franklin <slavo5150 yahoo.com> writes:
On Tuesday, 23 January 2018 at 21:53:24 UTC, Steven Schveighoffer 
wrote:

 Interestingly, `destroy` is an unsafe operation for classes.
Because it's calling a system function, rt_finalize. This function calls whatever is in the destructor, and because it works on Object level, it has no idea what the actual attributes of the derived destructor are. This needs to be fixed, but a whole host of issues like this exist with Object.
Are there any bugzilla issues that you are aware of that document this? Mike
Jan 23
prev sibling parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Tuesday, 23 January 2018 at 21:53:24 UTC, Steven Schveighoffer 
wrote:

 [1] Note: the reason they are safe is because they generally 
 result in a segfault, which doesn't harm any memory. This is 
 very much a user-space POV, and doesn't take into account 
 kernel-space where null dereferences may actually be valid 
 memory! It also doesn't (currently) take into account possible 
 huge objects that could extend into valid memory space, even in 
 user space.
That's what kindof ticks me off about this "null is memory safe" argument; it seems to be only applicable to a specific platform and environment. I have a micocontroller in front of me where an address of null (essentially 0) is a perfectly valid memory address. Mike
Jan 23
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/23/2018 4:42 PM, Mike Franklin wrote:
 That's what kindof ticks me off about this "null is memory safe" argument; it 
 seems to be only applicable to a specific platform and environment.
It's an extremely useful argument, though, as modern computers have virtual memory systems that map 0 to a seg fault, and have since the 80's, specifically because it DOES catch lots and lots of bugs. I always thought the IBM PC should have put the ROMs at address 0 instead of FFFF0. It probably would have saved billions of dollars.
 I have a micocontroller in front of me where an address of null (essentially
0) is a 
 perfectly valid memory address.
Microcontroller code tends to be small and so it's unlikely that you'll need to worry about it.
Jan 23
parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Wednesday, 24 January 2018 at 01:44:51 UTC, Walter Bright 
wrote:

 Microcontroller code tends to be small and so it's unlikely 
 that you'll need to worry about it.
I think you need to get involved in programming microcontrollers again because the landscape has changed drastically. The microcontrollers I use now are more powerful than PCs of the 90's. The project I'm currently working on is an HMI for industrial control with a full touchscreen 2D GUI. The code base is 240,084 lines of code and that doesn't even include the 3rd party libraries I'm using (e.g. 2D graphics library, newlib C library, FreeType font rendering library). That's not "small" by my standard of measure. And with devices such as this being increasingly connected to the Internet, such carelessness can easily be exploited as evident in https://en.wikipedia.org/wiki/2016_Dyn_cyberattack And that's not to mention the types of critical systems that run on such platforms that we are increasingly becoming more dependent on. We better start worrying about it. Mike
Jan 23
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, January 24, 2018 02:28:12 Mike Franklin via Digitalmars-d 
wrote:
 On Wednesday, 24 January 2018 at 01:44:51 UTC, Walter Bright

 wrote:
 Microcontroller code tends to be small and so it's unlikely
 that you'll need to worry about it.
I think you need to get involved in programming microcontrollers again because the landscape has changed drastically. The microcontrollers I use now are more powerful than PCs of the 90's. The project I'm currently working on is an HMI for industrial control with a full touchscreen 2D GUI. The code base is 240,084 lines of code and that doesn't even include the 3rd party libraries I'm using (e.g. 2D graphics library, newlib C library, FreeType font rendering library). That's not "small" by my standard of measure. And with devices such as this being increasingly connected to the Internet, such carelessness can easily be exploited as evident in https://en.wikipedia.org/wiki/2016_Dyn_cyberattack And that's not to mention the types of critical systems that run on such platforms that we are increasingly becoming more dependent on. We better start worrying about it.
Well, we can just mandate that dereferencing null be safe such that if it's not guaranteed that dereferencing null will segfault, the compiler will have to insert additional checks. We need to do that anyway for the overly large objects (and unfortunately don't last I heard). But as long as null checks aren't inserted when the target is going to segfault on dereferencing null, then we're not inserting unnecessary checks. That way, stuff running on a normal CPU would be the same as now (save for the objects that are too large for segfaulting to work), and targets like a microcontroller would get the extra checks so that they behaved more like if they were going to segfault on dereferencing null. But making dereferencing null system makes no sense, because that would mean that dereferencing pointers and references in general could not be safe. So, basically, anything that's not on the stack would then be system. And that would destroy safe. - Jonathan M Davis
Jan 23
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/23/2018 7:22 PM, Jonathan M Davis wrote:
 We need to do that anyway for the overly large
 objects (and unfortunately don't last I heard).
I put a limit in at one time for struct/class sizes to prevent this issue, but got a lot of pushback on it and it was reverted. Perhaps we can revisit that - and have large struct/classes be allow only in non- safe code. In general, though, if you don't have struct/class object sizes larger than the protected memory at null, you're safe with null dereferences.
Jan 24
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, January 24, 2018 18:46:38 Walter Bright via Digitalmars-d 
wrote:
 On 1/23/2018 7:22 PM, Jonathan M Davis wrote:
 We need to do that anyway for the overly large
 objects (and unfortunately don't last I heard).
I put a limit in at one time for struct/class sizes to prevent this issue, but got a lot of pushback on it and it was reverted. Perhaps we can revisit that - and have large struct/classes be allow only in non- safe code. In general, though, if you don't have struct/class object sizes larger than the protected memory at null, you're safe with null dereferences.
Yes, but we need to do _something_ about the overly large structs/classes if we want safe to be bulletproof like it's supposed to be (barring misuse of trusted, of course). I'd be inclined towards adding extra null-checks in those cases just because you'd end up with a balooning of system code in your code if we made dereferencing pointers/references to them system, but regardless, the important thing is that we do something with them (whatever that may be) so that we don't have code that the compiler claims to be safe and then goes and does something naughty with memory. Right now, those types are a lot like dynamic arrays with -noboundscheck except that the programmer didn't knowingly choose to be unsafe. Personally, I doubt that I've ever written code with types that large, but I really have no idea, because I don't know what the boundary is. I just know that I don't usually have really large types. But right now, it probably wouldn't be all that hard to shoot yourself in the foot by having a particularly large static array, and most folks would probably have no idea that they were making it so that they wouldn't get segfaults on null. The only reason that I know that that's possible is because of previous discussions on the topic here in the newsgroup, and I'm sure that plenty of other folks are in the same boat except that they haven't read those discussions and so still have no clue about it. - Jonathan M Davis
Jan 24
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/24/18 9:46 PM, Walter Bright wrote:
 On 1/23/2018 7:22 PM, Jonathan M Davis wrote:
 We need to do that anyway for the overly large
 objects (and unfortunately don't last I heard).
I put a limit in at one time for struct/class sizes to prevent this issue, but got a lot of pushback on it and it was reverted. Perhaps we can revisit that - and have large struct/classes be allow only in non- safe code. In general, though, if you don't have struct/class object sizes larger than the protected memory at null, you're safe with null dereferences.
You don't need to ban them from safe code, what you need to do is determine if the field itself is beyond the zero page (which causes a segfault), and if so, either read from the first byte of the struct (to cause the segfault if it's in there), or verify the struct's address is not within the zero page. We recently removed an assert for null this from all functions. Perhaps for structs that are large, in safe code add that check back. -Steve
Jan 25
prev sibling next sibling parent reply lobo <swamp.lobo gmail.com> writes:
On Wednesday, 24 January 2018 at 02:28:12 UTC, Mike Franklin 
wrote:
 On Wednesday, 24 January 2018 at 01:44:51 UTC, Walter Bright 
 wrote:

 Microcontroller code tends to be small and so it's unlikely 
 that you'll need to worry about it.
I think you need to get involved in programming microcontrollers again because the landscape has changed drastically. The microcontrollers I use now are more powerful than PCs of the 90's. The project I'm currently working on is an HMI for industrial control with a full touchscreen 2D GUI. The code base is 240,084 lines of code and that doesn't even include the 3rd party libraries I'm using (e.g. 2D graphics library, newlib C library, FreeType font rendering library). That's not "small" by my standard of measure. And with devices such as this being increasingly connected to the Internet, such carelessness can easily be exploited as evident in https://en.wikipedia.org/wiki/2016_Dyn_cyberattack And that's not to mention the types of critical systems that run on such platforms that we are increasingly becoming more dependent on. We better start worrying about it. Mike
Well if your embedded device has all that on it you should be sitting on an OS with proper memory management support. Even the hokey FreeRTOS can be configured to throw a hardware exception on nullptr access. I work on critical systems SW developing life support and pace makers. For us nullptrs and memory management is not an issue. It is not hard to design these problems out of the critical component architecture. The bigger problem is code logic bugs and for that we make heavy use of asserts and in-out contracts. We don't use D, it is all C++ and some Ada in the older systems. bye, lobo
Jan 23
parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Wednesday, 24 January 2018 at 03:46:41 UTC, lobo wrote:

 Well if your embedded device has all that on it you should be 
 sitting on an OS with proper memory management support.
I don't see how the OS can help if the underlying hardware doesn't have an MMU. That being said, admittedly, the more capable microcontrollers do have an MPU that can be configured to throw a hardware exception.
 We don't use D, it is all C++ and some Ada in the older systems.
Why don't you use D? Mike
Jan 23
parent reply lobo <swamplobo gmail.com> writes:
On Wednesday, 24 January 2018 at 04:15:27 UTC, Mike Franklin 
wrote:
 On Wednesday, 24 January 2018 at 03:46:41 UTC, lobo wrote:

 Well if your embedded device has all that on it you should be 
 sitting on an OS with proper memory management support.
I don't see how the OS can help if the underlying hardware doesn't have an MMU. That being said, admittedly, the more capable microcontrollers do have an MPU that can be configured to throw a hardware exception.
OK I'll state here that personally I don't agree with the segfault argument if the nullptr access can be detected at compile time. Anything that can be done at compile time should not be pushed out to runtime. That said you can architect code so that nullptrs go away and the MMU is not necessary. E.g. no pointers and no allocations after main() are just two of a number of steps you can take. Good engineering works; in the 10yrs I've been with the Health Care Devices group we haven't had one memory corruption issue in a critical component. The last memory corruption issue we had in non-critcal was 4yrs ago, in older C++ code. Memory corruption really is becoming a thing of the past in modern C++. Now the biggest problems for us are security because everything has to be internet enabled!
 We don't use D, it is all C++ and some Ada in the older 
 systems.
Why don't you use D? Mike
We're looking into D but at the moment but the general consensus is that the tooling is not mature enough on ARM (STM32) or Atmel AVR32 (used in our older devices). Rust is in the same boat. We have ~250 devs and there are basically three groups, C/C++, D and Rust. But it pains me to say that all three groups agree that modern C++ is probably going to win in the end. And I'm broken after using D, going back to C++ is awful and Rust just has too much friction to be enjoyable. bye, lobo
Jan 24
next sibling parent Kagamin <spam here.lot> writes:
On Wednesday, 24 January 2018 at 09:35:44 UTC, lobo wrote:
 The last memory corruption issue we had in non-critcal was 4yrs 
 ago, in older C++ code. Memory corruption really is becoming a 
 thing of the past in modern C++.
If you write everything from scratch with safety-oriented design?
Jan 24
prev sibling parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Wednesday, 24 January 2018 at 09:35:44 UTC, lobo wrote:

 And I'm broken after using D, going back to C++ is awful and 
 Rust just has too much friction to be enjoyable.
Yep, I know exactly what you mean.
Jan 25
next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 26, 2018 at 01:08:10AM +0000, Mike Franklin via Digitalmars-d wrote:
 On Wednesday, 24 January 2018 at 09:35:44 UTC, lobo wrote:
 
 And I'm broken after using D, going back to C++ is awful and Rust
 just has too much friction to be enjoyable.
Yep, I know exactly what you mean.
Me Too (tm). After having gotten used to D, working with C/C++ (or just about any other language, really) is just extremely painful. Unfortunately, I have no choice because my day job requires C/C++. D has officially ruined my life. :-D T -- Notwithstanding the eloquent discontent that you have just respectfully expressed at length against my verbal capabilities, I am afraid that I must unfortunately bring it to your attention that I am, in fact, NOT verbose.
Jan 25
prev sibling next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, January 25, 2018 17:20:21 H. S. Teoh via Digitalmars-d wrote:
 On Fri, Jan 26, 2018 at 01:08:10AM +0000, Mike Franklin via Digitalmars-d 
wrote:
 On Wednesday, 24 January 2018 at 09:35:44 UTC, lobo wrote:
 And I'm broken after using D, going back to C++ is awful and Rust
 just has too much friction to be enjoyable.
Yep, I know exactly what you mean.
Me Too (tm). After having gotten used to D, working with C/C++ (or just about any other language, really) is just extremely painful. Unfortunately, I have no choice because my day job requires C/C++. D has officially ruined my life. :-D
Well at least you don't have to program in Java. :) Unless something has changed in one of the recent versions, you can't even write a swap function in Java. :| It's definitely painful to have to program in C++ after programming in D, but I still find C++ to be more pleasant than the alternatives other than D. - Jonathan M Davis
Jan 26
prev sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Jan 26, 2018 at 04:36:18AM -0700, Jonathan M Davis via Digitalmars-d
wrote:
 On Thursday, January 25, 2018 17:20:21 H. S. Teoh via Digitalmars-d wrote:
 On Fri, Jan 26, 2018 at 01:08:10AM +0000, Mike Franklin via Digitalmars-d 
wrote:
 On Wednesday, 24 January 2018 at 09:35:44 UTC, lobo wrote:
 And I'm broken after using D, going back to C++ is awful and
 Rust just has too much friction to be enjoyable.
Yep, I know exactly what you mean.
Me Too (tm). After having gotten used to D, working with C/C++ (or just about any other language, really) is just extremely painful. Unfortunately, I have no choice because my day job requires C/C++. D has officially ruined my life. :-D
Well at least you don't have to program in Java. :) Unless something has changed in one of the recent versions, you can't even write a swap function in Java. :|
I haven't touched Java in a long while, but the last time I looked, it wasn't too horrible of a language. Needlessly verbose, yes. Breaks DRY, yes. Shoehorns everything into an OO model, even where it doesn't fit, yes. But in terms of the language itself, it's kinda pretty in its own way, even if it's in an idealistic, detached-from-the-real-world kind of way. At least you're not worried about buffer overruns, memory leaks, and inscrutible pointer bugs that could literally be *anywhere* in the entire 20,000-file codebase. Overall, I'd say Java is an OK language, not horrible, but not that great either. The only thing that makes it shine is really the wealth of libraries out there that you can draw from. I'd rate it as a "meh", whereas C is pretty horrible in spite of being extremely powerful, and C++ is just masochistic (though I confess I haven't looked into its latest incarnations -- the C++ code I have to work with dates back to C++98 and probably isn't going to change anytime in the foreseeable future).
 It's definitely painful to have to program in C++ after programming in
 D, but I still find C++ to be more pleasant than the alternatives
 other than D.
[...] I'm torn between whether C or C++ is worse. In some ways, I actually prefer C because the language is smaller, the semantics are more straightforward, and the potential dangers are well-known and well-studied. It doesn't lessen the pain, but at least you have well-established maps with which to navigate through the minefield. C++, OTOH... perhaps my opinion is biased by having had the misfortune of working with an overengineered, overdesigned C++ codebase that exemplified all the flaws of C++ and none of its advantages (thankfully, said codebase has been replaced... good riddance *shudder*). But when you're dealing with code where useful work is done inside dtors and where making a conceptual function call involves 6 layers of abstraction, one step of which involves fwrite()'ing parameters into a temporary file and fread()'ing from the other end, the only thing that can possibly come to mind is "where's my 10-foot pole and why am I not using it", and "is it even humanly possible to understand what this code actually does?!". C++ is just far too big, far too complex for any mortal to fully comprehend, and that's not even beginning to touch the semantics of a convoluted codebase that abuses the language in every possible way. No thanks, if I had the choice, I'm staying away from C++ as far as I possibly can. T -- MAS = Mana Ada Sistem?
Jan 26
prev sibling next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/23/18 9:28 PM, Mike Franklin wrote:
 On Wednesday, 24 January 2018 at 01:44:51 UTC, Walter Bright wrote:
 
 Microcontroller code tends to be small and so it's unlikely that 
 you'll need to worry about it.
I think you need to get involved in programming microcontrollers again because the landscape has changed drastically.  The microcontrollers I use now are more powerful than PCs of the 90's. The project I'm currently working on is an HMI for industrial control with a full touchscreen 2D GUI.  The code base  is 240,084 lines of code and that doesn't even include the 3rd party libraries I'm using (e.g. 2D graphics library, newlib C library, FreeType font rendering library). That's not "small" by my standard of measure. And with devices such as this being increasingly connected to the Internet, such carelessness can easily be exploited as evident in https://en.wikipedia.org/wiki/2016_Dyn_cyberattack   And that's not to mention the types of critical systems that run on such platforms that we are increasingly becoming more dependent on. We better start worrying about it.
While I understand your argument, the truth is that avoiding null dereferencing *statically* has to be built into the language from the beginning. As D is already too far along to retrofit this, your 2 options are: a) instrument the code, as Jonathan suggests (every dereference checks for null ahead of time). b) restrict your code, design, and functions that you use to ensure null pointers cannot happen. a) is something we could implement in D, and I think it might make sense as a specialized version of the compiler for certain situations. b) is something you can do in any language, and D gives you much of the tools to do so. Even implementing features of the compiler to help with option b is feasible, but I don't know what that is. An example that is slightly unrelated but on the same path: D arrays throw an error when you access an out-of-bounds value. An error is not recoverable, which means that the entire process has to die, or face undefined behavior. For vibe.d programs, this means killing the whole server if one route is implemented incorrectly. While you can restart the server, any in-progress calls will also be killed, unnecessarily. My solution to this was to create an array type that decays to a real array, but where out-of-bounds indexing throws an exception instead. I just have to be diligent about using this array type anywhere it might be an issue, and the problem is solved. And in fact, it was quite easy to do, due to the awesome powers of introspection in D. -Steve
Jan 24
parent reply Seb <seb wilzba.ch> writes:
On Wednesday, 24 January 2018 at 19:12:50 UTC, Steven 
Schveighoffer wrote:
 While I understand your argument, the truth is that avoiding 
 null dereferencing *statically* has to be built into the 
 language from the beginning. As D is already too far along to 
 retrofit this, your 2 options are:

 a) instrument the code, as Jonathan suggests (every dereference 
 checks for null ahead of time).

 b) restrict your code, design, and functions that you use to 
 ensure null pointers cannot happen.
There's also: c) Improve/split the language by introducing -dip25 / -dip1000 and hope that people interested in memory safety will migrate their code to it.
Jan 24
parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, January 24, 2018 21:24:16 Seb via Digitalmars-d wrote:
 On Wednesday, 24 January 2018 at 19:12:50 UTC, Steven

 Schveighoffer wrote:
 While I understand your argument, the truth is that avoiding
 null dereferencing *statically* has to be built into the
 language from the beginning. As D is already too far along to
 retrofit this, your 2 options are:

 a) instrument the code, as Jonathan suggests (every dereference
 checks for null ahead of time).

 b) restrict your code, design, and functions that you use to
 ensure null pointers cannot happen.
There's also: c) Improve/split the language by introducing -dip25 / -dip1000 and hope that people interested in memory safety will migrate their code to it.
Those really have nothing to do with null pointers though. - Jonathan M Davis
Jan 24
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/23/2018 6:28 PM, Mike Franklin wrote:
 I think you need to get involved in programming microcontrollers again because 
 the landscape has changed drastically.  The microcontrollers I use now are
more 
 powerful than PCs of the 90's.
Ok, but are these devices with 0 being a valid address? It seems weird to me that any sane modern CPU design that can access megabytes of memory would have 0 be a valid address.
Jan 24
parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Thursday, 25 January 2018 at 02:41:53 UTC, Walter Bright wrote:

 Ok, but are these devices with 0 being a valid address?

 It seems weird to me that any sane modern CPU design that can 
 access megabytes of memory would have 0 be a valid address.
Yes, 0 is a valid address and typically points to ROM (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CHDBIJJE.html). "The initial stack pointer and the address of the reset handler must be located at 0x0 and 0x4 respectively." (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CHDBIJJE.html) So you read address 0, dereference it, and you're at the bottom of the stack. Some microcontrollers have an MPU to mitigate this. You can read one technique here: http://nuttx.org/doku.php?id=wiki:howtos:stm32-null-pointer But the MPU is an optional component, and many microcontrollers in the ARM Cortex-M family do not have one. Mike
Jan 24
parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Thursday, 25 January 2018 at 04:01:47 UTC, Mike Franklin wrote:

 "The initial stack pointer and the address of the reset handler 
 must be located at 0x0 and 0x4 respectively." 
 (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/CHDBIJJE.html)
Sorry! Wrong link. Try this one: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/ar01s01s01.html
Jan 24
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 1/24/2018 8:31 PM, Mike Franklin wrote:
 On Thursday, 25 January 2018 at 04:01:47 UTC, Mike Franklin wrote:
 "The initial stack pointer and the address of the reset handler must be 
 located at 0x0 and 0x4 respectively." 
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0 79b/ar01s01s01.html
"These values are then loaded into the appropriate CPU registers at reset." This implies a ROM must be located there. Else how do initial values get there?
Jan 24
next sibling parent Mike Franklin <slavo5150 yahoo.com> writes:
On Thursday, 25 January 2018 at 04:45:34 UTC, Walter Bright wrote:

 This implies a ROM must be located there. Else how do initial 
 values get there?
I'm not sure what you mean. When you upload your firmware to the MCU, it writes the initial stack pointer to address 0x00 and the address of the reset handler to 0x04. It is up the developer to set these values properly in the linker script (a.k.a scatter file). Mike
Jan 24
prev sibling parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Thursday, 25 January 2018 at 04:45:34 UTC, Walter Bright wrote:

 This implies a ROM must be located there. Else how do initial 
 values get there?
Yes, ROM is at address 0. Address 0 contains the initial stack pointer. So you read address 0, dereference it, and then do your damage. Mike
Jan 24
parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Thursday, 25 January 2018 at 04:59:55 UTC, Mike Franklin wrote:

 Yes, ROM is at address 0.  Address 0 contains the initial stack 
 pointer.  So you read address 0, dereference it, and then do 
 your damage.
Keep in mind too that the ROM, on these devices, is actually reprogrammable from the firmware itself, so one could do some clever exploitation of that feature to insert whatever they want into the product's firmware. Mike
Jan 24
parent Walter Bright <newshound2 digitalmars.com> writes:
On 1/24/2018 9:04 PM, Mike Franklin wrote:
 On Thursday, 25 January 2018 at 04:59:55 UTC, Mike Franklin wrote:
 
 Yes, ROM is at address 0.  Address 0 contains the initial stack pointer.  So 
 you read address 0, dereference it, and then do your damage.
This is from the "what were they thinking" school of CPU design. Blargh.
 Keep in mind too that the ROM, on these devices, is actually reprogrammable
from 
 the firmware itself, so one could do some clever exploitation of that feature
to 
 insert whatever they want into the product's firmware.
I've posted online many times that people creating embedded systems should put the firmware in ROM, so malware will not survive a reset. The riposte I get is the firmware must be rewritable from the internet in order to fix malware written to it from the internet :-)
Jan 24
prev sibling parent Kagamin <spam here.lot> writes:
On Monday, 22 January 2018 at 23:30:16 UTC, Aedt wrote:
 I was asked in Reddit 
 (https://www.reddit.com/r/learnprogramming/comments/7ru82l/i_was_thinking_of_using
d_haxe_or_another/) how would D handle the following similar D code. I'm
surprised that both dmd and ldc provides no warnings even with -w argument
passed.
Well, if you want to check much at compile time, you probably want SPARK or F* (fstar).
Jan 23