www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Best way to make a template function conditionally trusted

reply tsbockman <thomas.bockman gmail.com> writes:
Suppose I have a templated struct member function for which I can 
compute at compile-time when the function is memory safe, and 
when it is not. But, the compiler cannot correctly determine this 
automatically.

What is the best way to express this in code? Other than 
straight-up duplicating the implementation, the only answer I've 
come up with so far is to create a `private  system` 
implementation function, and then separate `public  system` and 
`public  trusted` wrappers with appropriate template constraints.

Is there a better way?
Apr 01 2021
next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 1 April 2021 at 22:35:01 UTC, tsbockman wrote:
 Suppose I have a templated struct member function for which I 
 can compute at compile-time when the function is memory safe, 
 and when it is not. But, the compiler cannot correctly 
 determine this automatically.

 What is the best way to express this in code? Other than 
 straight-up duplicating the implementation, the only answer 
 I've come up with so far is to create a `private  system` 
 implementation function, and then separate `public  system` and 
 `public  trusted` wrappers with appropriate template 
 constraints.

 Is there a better way?
Here's a technique I've used: // infer function attributes auto func(...) { // do your compile-time memory-safety check here enum bool shouldBeSystem = ...; static if (shouldBeSystem) { // force inference of system cast(void) () system {}(); } () trusted { // rest of code goes here }(); } As long as the compile-time check is correct, this is sound: the trusted lambda can be called from safe code if and only if `shouldBeSystem == false`.
Apr 01 2021
parent tsbockman <thomas.bockman gmail.com> writes:
On Friday, 2 April 2021 at 00:03:32 UTC, Paul Backus wrote:
 On Thursday, 1 April 2021 at 22:35:01 UTC, tsbockman wrote:
 Is there a better way?
Here's a technique I've used: ... As long as the compile-time check is correct, this is sound: the trusted lambda can be called from safe code if and only if `shouldBeSystem == false`.
Thanks. That is somewhat better than my version.
Apr 01 2021
prev sibling parent reply ikod <igor.khasilev gmail.com> writes:
On Thursday, 1 April 2021 at 22:35:01 UTC, tsbockman wrote:
 Suppose I have a templated struct member function for which I 
 can compute at compile-time when the function is memory safe, 
 and when it is not. But, the compiler cannot correctly 
 determine this automatically.
Compiler should be able to derive safety of templated functions. You may just omit safe/ trusted. But there are some rules and restrictions: https://dlang.org/spec/function.html#function-attribute-inference
Apr 02 2021
parent tsbockman <thomas.bockman gmail.com> writes:
On Friday, 2 April 2021 at 19:49:30 UTC, ikod wrote:
 On Thursday, 1 April 2021 at 22:35:01 UTC, tsbockman wrote:
 Suppose I have a templated struct member function for which I 
 can compute at compile-time when the function is memory safe, 
 and when it is not. But, the compiler cannot correctly 
 determine this automatically.
Compiler should be able to derive safety of templated functions. You may just omit ` safe`/` trusted`.
The compiler's approach to verifying memory safety is very simplistic: it declares a function non-` safe` if any potentially unsafe operation is found in its implementation, without regard for the context. It can only ever infer ` safe` or ` system`, never ` trusted`. The reason ` trusted` is in the language at all is to allow the programmer to manually mark as memory safe those functions which contain operations that would be unsafe in some other context, but which the programmer has manually analyzed and verified to be incapable of violating memory safety, no matter what inputs it receives. Consider the following program: ```D import std.stdio; void writeSafe()(int[2] stuff ...) { foreach(n; 0 .. stuff.length) writeln(stuff[n]); } void writeTrusted()(int[2] stuff ...) { foreach(n; 0 .. stuff.length) writeln(stuff.ptr[n]); } void main() safe { writeSafe(3, 5); writeTrusted(3, 5); } ``` `writeSafe` and `writeTrusted` generate identical code, and are equally memory safe in reality. The compiler even knows this on some level, because it correctly deduces that the bounds check for `stuff[n]` can never fail, and omits it even in `writeSafe`. Nevertheless, because `.ptr` can be used to violate memory safety in other contexts, `writeSafe` is inferred as ` safe`, while `writeTrusted` is inferred as ` system`. And so, the program above will not compile as it stands. This is, of course, a trivial example where there is no benefit to using the non-` safe` version, but there are more complex cases where the desired algorithm is memory safe as a whole, but it cannot be expressed in D without using some operations that are forbidden in ` safe` code.
Apr 03 2021