www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Are contracts intended for verifying safety;

reply Somebody <ajieskola gmail.com> writes:
void moveFrom(int[] a, in int[] b)  trusted in{
     assert(a.length >= b.length);
} body {
     memmove(cast(void*)a.ptr, cast(void*)b.ptr, b.length * 
int.sizeof);
}

Is this ok? And if not, how should it be done, preferably without 
changing the condition or memmove call?
Nov 07 2016
next sibling parent reply ag0aep6g <anonymous example.com> writes:
On 11/07/2016 06:30 PM, Somebody wrote:
 void moveFrom(int[] a, in int[] b)  trusted in{
     assert(a.length >= b.length);
 } body {
     memmove(cast(void*)a.ptr, cast(void*)b.ptr, b.length * int.sizeof);
 }

 Is this ok? And if not, how should it be done, preferably without
 changing the condition or memmove call?
No, it's not ok. Contracts and (most) asserts are not included in release builds, but the rules for safe/ trusted don't change. So you'd be breaking safe in release builds. You can use std.exception.enforce instead, in the body instead of a contract: void moveFrom(int[] a, in int[] b) trusted { import std.exception: enforce; enforce(a.length >= b.length); memmove(/* ... as above ... */); }
Nov 07 2016
parent reply Somebody <ajieskola gmail.com> writes:
On Monday, 7 November 2016 at 19:30:01 UTC, ag0aep6g wrote:
 No, it's not ok. Contracts and (most) asserts are not included 
 in release builds, but the rules for  safe/ trusted don't 
 change. So you'd be breaking  safe in release builds.

 You can use std.exception.enforce instead, in the body instead 
 of a contract:

 void moveFrom(int[] a, in int[] b)  trusted
 {
     import std.exception: enforce;
     enforce(a.length >= b.length);
     memmove(/* ... as above ... */);
 }
Can the version switch be used to detect noboundscheck? I was thinking that if, it could be used to make an overload of enforce that acts just like in your example, but is taken off with bounds checking. Then it would not violate safety any more than arrays do, yet could be compiled to as efficient a release build as the contract version, if needed.
Nov 07 2016
next sibling parent Somebody <ajieskola gmail.com> writes:
...and it would, unlike enforce(), of course throw an Error 
instead of an Exception.
Nov 07 2016
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 11/07/2016 09:16 PM, Somebody wrote:
 Can the version switch be used to detect noboundscheck?
Apparently, yes: `version (D_NoBoundsChecks)`. http://dlang.org/spec/version.html#predefined-versions
 I was thinking
 that if, it could be used to make an overload of enforce that acts just
 like in your example, but is taken off with bounds checking. Then it
 would not violate safety any more than arrays do, yet could be compiled
 to as efficient a release build as the contract version, if needed.
You're probably aware of it, but just to be sure: Note that -noboundscheck (or -boundscheck=off) absolutely breaks safety.
Nov 07 2016
parent Somebody <ajieskola gmail.com> writes:
On Monday, 7 November 2016 at 20:42:26 UTC, ag0aep6g wrote:
 Apparently, yes: `version (D_NoBoundsChecks)`.

 http://dlang.org/spec/version.html#predefined-versions


 You're probably aware of it, but just to be sure: Note that 
 -noboundscheck (or -boundscheck=off) absolutely breaks safety.
Yes I am. Using that is in my understanding unwise in any trivial project. Thanks for your help and the warning. This anyway seems to let choose the best tradeoff when compiling, unlike enforce, assert in system or my original contracted version.
Nov 07 2016
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, November 07, 2016 17:30:09 Somebody via Digitalmars-d-learn 
wrote:
 void moveFrom(int[] a, in int[] b)  trusted in{
      assert(a.length >= b.length);
 } body {
      memmove(cast(void*)a.ptr, cast(void*)b.ptr, b.length *
 int.sizeof);
 }

 Is this ok? And if not, how should it be done, preferably without
 changing the condition or memmove call?
That's a matter of debate. What you're saying is that the contract for the function requires certain conditions be true for the function arguments, and if they are true, then the function is safe. And that's legitimate. But at the same time, it doesn't absolutely guarantee safety, because if the caller passes arguments that violate the contract, and the function is compiled with -release, then unsafe things will occur. So, you could mark the function as system, and then make it clear in the documentation that the function's safety depends on the function's arguments meeting the pre-condition. So, the caller then knows when they can mark the call as being trusted. There have been several discussions about trusted over the last couple of years (both in the newsgroup and on github), and I don't know what the official stance on this sort of thing currently is. I know that at one point, it was argued that trusted functions were safe so long as their arguments were valid, but then you get stuff like auto foo(int* arr, size_t index) trusted { return *(arr + index); } or auto foo(int* arr, size_t length) trusted { return arr[0 .. length]; } or auto foo(int[], size_t index) trusted { return *(arr.ptr + index); } and it doesn't seem like a good idea to me to mark functions like that as trusted. It's too much like you're trying to have array indexing be marked as trusted while circumventing the array bounds checks without any guarantee that the values are going to be valid. So, while I don't know what the official stance is, I'd suggest having the function be trusted and having the documentation make it clear what the preconditions are so that the calling function can be marked trusted. - Jonathan M Davis
Nov 07 2016
parent reply Somebody <ajieskola gmail.com> writes:
On Monday, 7 November 2016 at 20:24:25 UTC, Jonathan M Davis 
wrote:
 That's a matter of debate. What you're saying is that the 
 contract for the function requires certain conditions be true 
 for the function arguments, and if they are true, then the 
 function is  safe. And that's legitimate. But at the same time, 
 it doesn't absolutely guarantee  safety, because if the caller 
 passes arguments that violate the contract, and the function is 
 compiled with -release, then unsafe things will occur. So, you 
 could mark the function as  system, and then make it clear in 
 the documentation that the function's  safety depends on the 
 function's arguments meeting the pre-condition. So, the caller 
 then knows when they can mark the call as being  trusted.

 There have been several discussions about  trusted over the 
 last couple of years (both in the newsgroup and on github), and 
 I don't know what the official stance on this sort of thing 
 currently is. I know that at one point, it was argued that 
  trusted functions were  safe so long as their arguments were 
 valid, but then you get stuff like

 auto foo(int* arr, size_t index)  trusted
 {
     return *(arr + index);
 }

 or

 auto foo(int* arr, size_t length)  trusted
 {
     return arr[0 .. length];
 }

 or

 auto foo(int[], size_t index)  trusted
 {
     return *(arr.ptr + index);
 }

 and it doesn't seem like a good idea to me to mark functions 
 like that as  trusted. It's too much like you're trying to have 
 array indexing be marked as  trusted while circumventing the 
 array bounds checks without any guarantee that the values are 
 going to be valid.
All pure functions which a programmer, save for a blackhat perhaps, has any reason to use are safe with correct arguments. So we would have no use for safe when we have pure, were that argument about arguments wise to follow. So I agree with you that they definitely would be a bad idea without contracts. However, with contracts, as you said, it is less clear.
 So, while I don't know what the official stance is, I'd suggest 
 having the function be  trusted and having the documentation 
 make it clear what the preconditions are so that the calling 
 function can be marked  trusted.

 - Jonathan M Davis
I reckon you meant marking the calling function safe?
Nov 07 2016
parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Monday, November 07, 2016 20:38:00 Somebody via Digitalmars-d-learn 
wrote:
 So, while I don't know what the official stance is, I'd suggest
 having the function be  trusted and having the documentation
 make it clear what the preconditions are so that the calling
 function can be marked  trusted.
I reckon you meant marking the calling function safe?
No. trusted. The calling function could only be marked safe if the callee were trusted, and I was suggesting that it be marked system so that it's then clear to the caller that they need to pass the correct arguments for it to be okay to treat it as safe and mark the caller as trusted. If need be, the contract can be documented to make it clear what's required for it to be reasonable to mark the caller as trusted. - Jonathan M Davis
Nov 07 2016
parent Somebody <ajieskola gmail.com> writes:
On Monday, 7 November 2016 at 21:05:32 UTC, Jonathan M Davis 
wrote:
 No.  trusted. The calling function could only be marked  safe 
 if the callee were  trusted, and I was suggesting that it be 
 marked  system so that it's then clear to the caller that they 
 need to pass the correct arguments for it to be okay to treat 
 it as  safe and mark the caller as  trusted. If need be, the 
 contract can be documented to make it clear what's required for 
 it to be reasonable to mark the caller as  trusted.

 - Jonathan M Davis
Oh, I see. I think I prefer to use the modified enforce trough (see above), so I have no need to make trusted layers in the calling site. Thanks anyway.
Nov 07 2016