www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Asserts and side effects

reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
The following is a quote from a comment in the Reddit post about asserts 
that Walter just linked to:

     "Asserts are not without pitfalls, though. If you aren't careful,
      what to put in the asserts, the program could behave differently"

A trivial example of this is:

     int i;
     write(i);
     assert (++i == 1);
     write(i);

Normally, this program prints "01", but in release mode it prints "00". 
Asserts should never change the behaviour of a program, but currently it 
is up to the programmer to verify that this is in fact the case.

Would it be possible (and desirable) for the D compiler to statically 
check that asserts have no side effects?

-Lars
Nov 30 2009
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Lars T. Kyllingstad:
 Would it be possible (and desirable) for the D compiler to statically 
 check that asserts have no side effects?
Only pure asserts? If you put: assert (++i == 1); Inside a function precondition, it gets removed in release mode. Bye, bearophile
Nov 30 2009
parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
bearophile wrote:
 Lars T. Kyllingstad:
 Would it be possible (and desirable) for the D compiler to statically 
 check that asserts have no side effects?
Only pure asserts?
Like Denis said, pure is a bit too strict. One just wants to make sure that the assert doesn't *change* any variables. It should be able to read them, otherwise there would be no point in it. -Lars
Dec 01 2009
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 30 Nov 2009 13:52:48 +0300, Lars T. Kyllingstad  
<public kyllingen.nospamnet> wrote:

 The following is a quote from a comment in the Reddit post about asserts  
 that Walter just linked to:

      "Asserts are not without pitfalls, though. If you aren't careful,
       what to put in the asserts, the program could behave differently"

 A trivial example of this is:

      int i;
      write(i);
      assert (++i == 1);
      write(i);

 Normally, this program prints "01", but in release mode it prints "00".  
 Asserts should never change the behaviour of a program, but currently it  
 is up to the programmer to verify that this is in fact the case.

 Would it be possible (and desirable) for the D compiler to statically  
 check that asserts have no side effects?

 -Lars
Compiler must do full code flow analysis to do that: bool foo(); // no body or body is very complicated void main() { assert(foo()); // okay to call or not? } I believe this is doable with introduction of a new attribute - hasNoSideEffects (shorter name would be better) with the following rules: - hasNoSideEffects functions cannot modify variables outside of a function scope. It means that they can read globals, but can not get a non-const reference (or pointer) to them. - hasNoSideEffects can only call other hasNoSideEffects functions - Body of an assert check has a hasNoSideEffects attribute, and follows the same rules. pure is a subclass of hasNoSideEffects. hasNoSideEffects functions are also very useful for various performance optimizations. I think it is as valid attribute as pure (even though it might affect code generation outside of a function body due to open optimization opportunities). Perhaps pure semantics should be changed to hasNoSideEffects (i.e. allow non-invariant input arguments and allow accessing globals). It will lose a memoization ability, but to be honest I don't think memoization needs to be implemented based on pure attribute alone. For example, std.math.pow() is pure, but is it worth memoizing? I think it's programmer who should decide what functions are expensive enough to be memoized, and these should be marked as such explicitly ( memoizable, or something).
Nov 30 2009
parent reply KennyTM~ <kennytm gmail.com> writes:
On Nov 30, 09 19:15, Denis Koroskin wrote:
 On Mon, 30 Nov 2009 13:52:48 +0300, Lars T. Kyllingstad
 <public kyllingen.nospamnet> wrote:

 The following is a quote from a comment in the Reddit post about
 asserts that Walter just linked to:

 "Asserts are not without pitfalls, though. If you aren't careful,
 what to put in the asserts, the program could behave differently"

 A trivial example of this is:

 int i;
 write(i);
 assert (++i == 1);
 write(i);

 Normally, this program prints "01", but in release mode it prints
 "00". Asserts should never change the behaviour of a program, but
 currently it is up to the programmer to verify that this is in fact
 the case.

 Would it be possible (and desirable) for the D compiler to statically
 check that asserts have no side effects?

 -Lars
Compiler must do full code flow analysis to do that: bool foo(); // no body or body is very complicated void main() { assert(foo()); // okay to call or not? } I believe this is doable with introduction of a new attribute - hasNoSideEffects (shorter name would be better) with the following rules: - hasNoSideEffects functions cannot modify variables outside of a function scope. It means that they can read globals, but can not get a non-const reference (or pointer) to them. - hasNoSideEffects can only call other hasNoSideEffects functions - Body of an assert check has a hasNoSideEffects attribute, and follows the same rules. pure is a subclass of hasNoSideEffects. hasNoSideEffects functions are also very useful for various performance optimizations. I think it is as valid attribute as pure (even though it might affect code generation outside of a function body due to open optimization opportunities). Perhaps pure semantics should be changed to hasNoSideEffects (i.e. allow non-invariant input arguments and allow accessing globals). It will lose a memoization ability, but to be honest I don't think memoization needs to be implemented based on pure attribute alone. For example, std.math.pow() is pure, but is it worth memoizing? I think it's programmer who should decide what functions are expensive enough to be memoized, and these should be marked as such explicitly ( memoizable, or something).
Can you give an example which hasNoSideEffects but not pure?
Nov 30 2009
parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Mon, 30 Nov 2009 15:46:45 +0300, KennyTM~ <kennytm gmail.com> wrote:

 On Nov 30, 09 19:15, Denis Koroskin wrote:
 On Mon, 30 Nov 2009 13:52:48 +0300, Lars T. Kyllingstad
 <public kyllingen.nospamnet> wrote:

 The following is a quote from a comment in the Reddit post about
 asserts that Walter just linked to:

 "Asserts are not without pitfalls, though. If you aren't careful,
 what to put in the asserts, the program could behave differently"

 A trivial example of this is:

 int i;
 write(i);
 assert (++i == 1);
 write(i);

 Normally, this program prints "01", but in release mode it prints
 "00". Asserts should never change the behaviour of a program, but
 currently it is up to the programmer to verify that this is in fact
 the case.

 Would it be possible (and desirable) for the D compiler to statically
 check that asserts have no side effects?

 -Lars
Compiler must do full code flow analysis to do that: bool foo(); // no body or body is very complicated void main() { assert(foo()); // okay to call or not? } I believe this is doable with introduction of a new attribute - hasNoSideEffects (shorter name would be better) with the following rules: - hasNoSideEffects functions cannot modify variables outside of a function scope. It means that they can read globals, but can not get a non-const reference (or pointer) to them. - hasNoSideEffects can only call other hasNoSideEffects functions - Body of an assert check has a hasNoSideEffects attribute, and follows the same rules. pure is a subclass of hasNoSideEffects. hasNoSideEffects functions are also very useful for various performance optimizations. I think it is as valid attribute as pure (even though it might affect code generation outside of a function body due to open optimization opportunities). Perhaps pure semantics should be changed to hasNoSideEffects (i.e. allow non-invariant input arguments and allow accessing globals). It will lose a memoization ability, but to be honest I don't think memoization needs to be implemented based on pure attribute alone. For example, std.math.pow() is pure, but is it worth memoizing? I think it's programmer who should decide what functions are expensive enough to be memoized, and these should be marked as such explicitly ( memoizable, or something).
Can you give an example which hasNoSideEffects but not pure?
Sure: // can't be pure because pure functions // accept only immutable args hasNoSideEffects int deref(int* ptr) { return *ptr; } class Array { hasNoSideEffects int length() const { return _array.length; } hasNoSideEffects ref T opIndex(size_t index) { return _array[index]; } private T[] _array; } Array!(int) array = ...; for (int i = 0; i < array.length(); ++i) { int v1 = array[i]; // int v2 = array[i]; int v3 = array[i]; } It the example above, array.length and i-th value are only evaluated once, because length and opIndex both have no side effects. BTW, any class invariant should also be marked as hasNoSideEffects implicitly and checked accordingly. I think pure attribute is too restrictive to be useful. I believe there are an order of magnitude more functions that could be marked as hasNoSideEffects than those that could be marked as pure.
Nov 30 2009
parent Don <nospam nospam.com> writes:
Denis Koroskin wrote:
 On Mon, 30 Nov 2009 15:46:45 +0300, KennyTM~ <kennytm gmail.com> wrote:
 
 On Nov 30, 09 19:15, Denis Koroskin wrote:
 On Mon, 30 Nov 2009 13:52:48 +0300, Lars T. Kyllingstad
 <public kyllingen.nospamnet> wrote:

 The following is a quote from a comment in the Reddit post about
 asserts that Walter just linked to:

 "Asserts are not without pitfalls, though. If you aren't careful,
 what to put in the asserts, the program could behave differently"

 A trivial example of this is:

 int i;
 write(i);
 assert (++i == 1);
 write(i);

 Normally, this program prints "01", but in release mode it prints
 "00". Asserts should never change the behaviour of a program, but
 currently it is up to the programmer to verify that this is in fact
 the case.

 Would it be possible (and desirable) for the D compiler to statically
 check that asserts have no side effects?

 -Lars
Compiler must do full code flow analysis to do that: bool foo(); // no body or body is very complicated void main() { assert(foo()); // okay to call or not? } I believe this is doable with introduction of a new attribute - hasNoSideEffects (shorter name would be better) with the following rules: - hasNoSideEffects functions cannot modify variables outside of a function scope. It means that they can read globals, but can not get a non-const reference (or pointer) to them. - hasNoSideEffects can only call other hasNoSideEffects functions - Body of an assert check has a hasNoSideEffects attribute, and follows the same rules. pure is a subclass of hasNoSideEffects. hasNoSideEffects functions are also very useful for various performance optimizations. I think it is as valid attribute as pure (even though it might affect code generation outside of a function body due to open optimization opportunities). Perhaps pure semantics should be changed to hasNoSideEffects (i.e. allow non-invariant input arguments and allow accessing globals). It will lose a memoization ability, but to be honest I don't think memoization needs to be implemented based on pure attribute alone. For example, std.math.pow() is pure, but is it worth memoizing? I think it's programmer who should decide what functions are expensive enough to be memoized, and these should be marked as such explicitly ( memoizable, or something).
Can you give an example which hasNoSideEffects but not pure?
Sure: // can't be pure because pure functions // accept only immutable args hasNoSideEffects int deref(int* ptr) { return *ptr; } class Array { hasNoSideEffects int length() const { return _array.length; } hasNoSideEffects ref T opIndex(size_t index) { return _array[index]; } private T[] _array; } Array!(int) array = ...; for (int i = 0; i < array.length(); ++i) { int v1 = array[i]; // int v2 = array[i]; int v3 = array[i]; } It the example above, array.length and i-th value are only evaluated once, because length and opIndex both have no side effects. BTW, any class invariant should also be marked as hasNoSideEffects implicitly and checked accordingly. I think pure attribute is too restrictive to be useful. I believe there are an order of magnitude more functions that could be marked as hasNoSideEffects than those that could be marked as pure.
Interestingly, provided source code is available, the CTFE engine can execute hasNoSideEffects functions. It doesn't require 'pure'.
Nov 30 2009