www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - safe - why does this compile?

reply Piotr Mitana <the.mail.of.mi2 gmail.com> writes:
This code:

     import std.stdio;

     class X1 {}
     class X2 : X1
     {
	void run()  safe
         {
             writeln("DONE");
         }
     }

     void main()  safe
     {
         X1 x1 = new X1;
         X2 x2 = cast(X2) x1;
         x2.run();
     }

is obviously wrong gets killed by OS's signal. Why is it  safe? I 
thought  safe should prevent such errors as well.
Jul 13 2018
next sibling parent reply ketmar <ketmar ketmar.no-ip.org> writes:
Piotr Mitana wrote:

 This code:

      import std.stdio;

      class X1 {}
      class X2 : X1
      {
 	void run()  safe
          {
              writeln("DONE");
          }
      }

      void main()  safe
      {
          X1 x1 = new X1;
          X2 x2 = cast(X2) x1;
          x2.run();
      }

 is obviously wrong gets killed by OS's signal. Why is it  safe? I thought 
  safe should prevent such errors as well.
there is nothing wrong here. dereferencing null reference is completely safe (in terms of result predictability).
Jul 13 2018
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/13/18 7:22 AM, ketmar wrote:
 Piotr Mitana wrote:
 
 This code:

      import std.stdio;

      class X1 {}
      class X2 : X1
      {
     void run()  safe
          {
              writeln("DONE");
          }
      }

      void main()  safe
      {
          X1 x1 = new X1;
          X2 x2 = cast(X2) x1;
          x2.run();
      }

 is obviously wrong gets killed by OS's signal. Why is it  safe? I 
 thought  safe should prevent such errors as well.
there is nothing wrong here. dereferencing null reference is completely safe (in terms of result predictability).
To emphasize the point, this is safe as well: X2 x2; // = null x2.run(); D does not consider a segmentation fault due to null dereferencing to be unsafe -- no memory corruption happens. -Steve
Jul 13 2018
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
Steven Schveighoffer wrote:

 To emphasize the point, this is  safe as well:

 X2 x2; // = null
 x2.run();

 D does not consider a segmentation fault due to null dereferencing to be 
 unsafe -- no memory corruption happens.
yeah. in simple words: safe code is *predictable*, but not "segfault-less". segfaults (null dereferences) in safe code are allowed, 'cause they have completely predictable behavior (instant program termination). safe doesn't free you from doing your null checks, it protects you from so-called "undefined behavior" (aka "unpredictable execution results"). so when we are talking about "memory safety", it doesn't mean that your code cannot segfault, it means that your code won't corrupt random memory due to misbehaving.
Jul 13 2018
parent reply Johan Engelen <j j.nl> writes:
On Friday, 13 July 2018 at 14:51:17 UTC, ketmar wrote:
 yeah. in simple words: safe code is *predictable*, but not 
 "segfault-less". segfaults (null dereferences) in safe code are 
 allowed, 'cause they have completely predictable behavior 
 (instant program termination).

  safe doesn't free you from doing your null checks, it protects 
 you from so-called "undefined behavior" (aka "unpredictable 
 execution results"). so when we are talking about "memory 
 safety", it doesn't mean that your code cannot segfault, it 
 means that your code won't corrupt random memory due to 
 misbehaving.
This is not true when using LDC (and I'd expect the same for GDC). With LDC, dereferencing `null` is undefined behavior regardless of whether you are in an safe context or not. - Johan
Jul 16 2018
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
Johan Engelen wrote:

 On Friday, 13 July 2018 at 14:51:17 UTC, ketmar wrote:
 yeah. in simple words: safe code is *predictable*, but not 
 "segfault-less". segfaults (null dereferences) in safe code are allowed, 
 'cause they have completely predictable behavior (instant program 
 termination).

  safe doesn't free you from doing your null checks, it protects you from 
 so-called "undefined behavior" (aka "unpredictable execution results"). 
 so when we are talking about "memory safety", it doesn't mean that your 
 code cannot segfault, it means that your code won't corrupt random 
 memory due to misbehaving.
This is not true when using LDC (and I'd expect the same for GDC). With LDC, dereferencing `null` is undefined behavior regardless of whether you are in an safe context or not. - Johan
then those compilers are broken, and should be fixed.
Jul 16 2018
prev sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
Johan Engelen wrote:

 On Friday, 13 July 2018 at 14:51:17 UTC, ketmar wrote:
 yeah. in simple words: safe code is *predictable*, but not 
 "segfault-less". segfaults (null dereferences) in safe code are allowed, 
 'cause they have completely predictable behavior (instant program 
 termination).

  safe doesn't free you from doing your null checks, it protects you from 
 so-called "undefined behavior" (aka "unpredictable execution results"). 
 so when we are talking about "memory safety", it doesn't mean that your 
 code cannot segfault, it means that your code won't corrupt random 
 memory due to misbehaving.
This is not true when using LDC (and I'd expect the same for GDC). With LDC, dereferencing `null` is undefined behavior regardless of whether you are in an safe context or not. - Johan
p.s.: it worth noting that *program* *state* is undefined after null dereference, though. i.e. program cannot continue execution, and must be aborted. in this sense, null dereferencing is defined behavior: it aborts the app unconditionally. and if you will catch segfault with some OS mechanics, you still cannot reliably do *anything* except immediately aborting (strictly speaking, this is true for any `Error` condition, even for asserts). so compiler *can* assume that null dereferencing is something code generally won't do, but it cannot do any optimisations assuming that code will not dereference nulls at all (dmd -O, afair, was guilty of some such optimisations too). so, code can dereference null, and it will be immediately aborted, without any chance to perform cleanup (as program state is undefined after this operation). in this sense, null dereferencing is "defined behavior".
Jul 16 2018
prev sibling next sibling parent bauss <jj_1337 live.dk> writes:
On Friday, 13 July 2018 at 11:04:40 UTC, Piotr Mitana wrote:
 This code:

     import std.stdio;

     class X1 {}
     class X2 : X1
     {
 	void run()  safe
         {
             writeln("DONE");
         }
     }

     void main()  safe
     {
         X1 x1 = new X1;
         X2 x2 = cast(X2) x1;
         x2.run();
     }

 is obviously wrong gets killed by OS's signal. Why is it  safe? 
 I thought  safe should prevent such errors as well.
See: https://dlang.org/spec/function.html#function-safety
Jul 13 2018
prev sibling parent reply Timoses <timosesu gmail.com> writes:
On Friday, 13 July 2018 at 11:04:40 UTC, Piotr Mitana wrote:
 This code:

     import std.stdio;

     class X1 {}
     class X2 : X1
     {
 	void run()  safe
         {
             writeln("DONE");
         }
     }

     void main()  safe
     {
         X1 x1 = new X1;
         X2 x2 = cast(X2) x1;
         x2.run();
     }

 is obviously wrong gets killed by OS's signal. Why is it  safe? 
 I thought  safe should prevent such errors as well.
I suppose this is another good example of how casting can be dangerous? E.g. also: immutable int i = 3; int* j = cast(int*)&i; assert(i == 3); *j = 4; assert(j == &i); // data occupies same address space assert(i == 3 && *j == 4); // yet the values differ
Jul 13 2018
parent reply Dukc <ajieskola gmail.com> writes:
On Friday, 13 July 2018 at 13:52:27 UTC, Timoses wrote:
 I suppose this is another good example of how casting can be 
 dangerous?

 E.g. also:

     immutable int i = 3;
     int* j = cast(int*)&i;
     assert(i == 3);
 	*j = 4;
     assert(j == &i); // data occupies same address space
     assert(i == 3 && *j == 4); // yet the values differ
No, casting classes to their subclasses is not dangerous to program integrity, because it is checked. It is just a regular bug that terminates the program when encountered. But casting away immutable can break program integrity as your example demonstrates. For that reason the compiler won't let you do that if you wrap that code in safe, unlike the class cast.
Jul 13 2018
parent reply Timoses <timosesu gmail.com> writes:
On Friday, 13 July 2018 at 22:17:59 UTC, Dukc wrote:
 On Friday, 13 July 2018 at 13:52:27 UTC, Timoses wrote:
 I suppose this is another good example of how casting can be 
 dangerous?

 E.g. also:

     immutable int i = 3;
     int* j = cast(int*)&i;
     assert(i == 3);
 	*j = 4;
     assert(j == &i); // data occupies same address space
     assert(i == 3 && *j == 4); // yet the values differ
No, casting classes to their subclasses is not dangerous to program integrity, because it is checked. It is just a regular bug that terminates the program when encountered. But casting away immutable can break program integrity as your example demonstrates. For that reason the compiler won't let you do that if you wrap that code in safe, unlike the class cast.
Thanks for the explanation. Only read the function safety chapter in depth after posting this : D. Still, is `cast`ing seen as something "dangerous" or as something that should only be done as a last resort? Should std.conv : to be prioritized?
Jul 13 2018
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/14/18 2:50 AM, Timoses wrote:
 On Friday, 13 July 2018 at 22:17:59 UTC, Dukc wrote:
 On Friday, 13 July 2018 at 13:52:27 UTC, Timoses wrote:
 I suppose this is another good example of how casting can be dangerous?

 E.g. also:

     immutable int i = 3;
     int* j = cast(int*)&i;
     assert(i == 3);
     *j = 4;
     assert(j == &i); // data occupies same address space
     assert(i == 3 && *j == 4); // yet the values differ
No, casting classes to their subclasses is not dangerous to program integrity, because it is checked. It is just a regular bug that terminates the program when encountered. But casting away immutable can break program integrity as your example demonstrates. For that reason the compiler won't let you do that if you wrap that code in safe, unlike the class cast.
Thanks for the explanation. Only read the function safety chapter in depth after posting this : D. Still, is `cast`ing seen as something "dangerous" or as something that should only be done as a last resort? Should std.conv : to be prioritized?
Well, std.conv.to is going to throw if it doesn't dynamically convert. So it depends on the behavior you want. But yeah, if you know it's going to work, you probably want to do std.conv.to, it's safer. Just FYI, it's kind of bad that you have to use cast here, because cast isn't going to distinguish between dynamic casting and const casting. It's kind of a problem in D in general. We don't have C++ niceties like const_cast etc. -Steve
Jul 14 2018