www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - trusting generic functions

reply Lodovico Giaretta <lodovico giaretart.net> writes:
Let's say I have a generic function that uses pointers. It will 
be inferred  system by the compiler, but I know that the pointer 
usage can be  trusted.
The problem is that if I declare the function  trusted, I'm also 
implicitly trusting any call to  system methods of the template 
parameter.

Dumb example (pointer usage can be trusted, but doSomething may 
not):

     auto doSomethingDumb(T)(ref T t) // I want it  trusted if 
doSomething is at least  trusted
     {
         T* pt = &t;
         return pt.doSomething();
     }

Is there any way around this? Any way to declare a function 
 trusted as long as the methods of the template argument are at 
least  trusted?

Thank you in advance.

Lodovico Giaretta
May 28 2016
next sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 11:50:33 UTC, Lodovico Giaretta wrote:
 Is there any way around this? Any way to declare a function 
  trusted as long as the methods of the template argument are at 
 least  trusted?

 Thank you in advance.
Use traits.. https://dlang.org/phobos/std_traits.html#isSafe so your function becomes (i believe) auto doSomethingDumb(T)(ref T t) if(isSafe!(T))
May 28 2016
next sibling parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 11:57:09 UTC, Era Scarecrow wrote:
     auto doSomethingDumb(T)(ref T t) if(isSafe!(T))
Should also probably test for a function or delegate. So...? auto doSomethingDumb(T)(ref T t) if(isSafe!T && (isFunctionPointer!T || isDelegate!T)) { T* pt = &t; return pt.doSomething(); }
May 28 2016
prev sibling parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Saturday, 28 May 2016 at 11:57:09 UTC, Era Scarecrow wrote:
  Use traits..

 https://dlang.org/phobos/std_traits.html#isSafe

 so your function becomes (i believe)


     auto doSomethingDumb(T)(ref T t) if(isSafe!(T))
The problem is that T is a type, and I should check for safety of every method of T that I'm using in my function. This does not scale well, and if I change the body of the function to use a new method, I may forget to add it to the isSafe checks.
May 28 2016
next sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 12:25:14 UTC, Lodovico Giaretta wrote:
 On Saturday, 28 May 2016 at 11:57:09 UTC, Era Scarecrow wrote:
     auto doSomethingDumb(T)(ref T t) if(isSafe!(T))
The problem is that T is a type, and I should check for safety of every method of T that I'm using in my function. This does not scale well, and if I change the body of the function to use a new method, I may forget to add it to the isSafe checks.
Hmmm that doesn't sound right. Safe code can't call unsafe code (or it shouldn't), it continues safe/ trusted all the way down. So if you are passed a safe/trusted function, then it shouldn't need other checks. Or am i reading the question wrong?
May 28 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Saturday, 28 May 2016 at 12:33:28 UTC, Era Scarecrow wrote:
 On Saturday, 28 May 2016 at 12:25:14 UTC, Lodovico Giaretta 
 wrote:
 On Saturday, 28 May 2016 at 11:57:09 UTC, Era Scarecrow wrote:
     auto doSomethingDumb(T)(ref T t) if(isSafe!(T))
The problem is that T is a type, and I should check for safety of every method of T that I'm using in my function. This does not scale well, and if I change the body of the function to use a new method, I may forget to add it to the isSafe checks.
Hmmm that doesn't sound right. Safe code can't call unsafe code (or it shouldn't), it continues safe/ trusted all the way down. So if you are passed a safe/trusted function, then it shouldn't need other checks. Or am i reading the question wrong?
Sorry for not being clear. Look at this example: struct S1 { int doSomething() safe { // do something safely return 1; } } struct S2 { int doSomething() system { // do something usafe return 2; } } auto doSomethingDumb(T)(ref T t) { T* pt = &t; return pt.doSomething(); } auto s1 = S1(); auto s2 = S2(); auto x = doSomethingDumb(s1); // this call should be possible in safe code auto y = doSomethingDumb(s2); // this should only be possible in system code
May 28 2016
parent ag0aep6g <anonymous example.com> writes:
On 05/28/2016 02:43 PM, Lodovico Giaretta wrote:
 struct S1
 {
      int doSomething()  safe
      {
          // do something safely
          return 1;
      }
 }

 struct S2
 {
      int doSomething()  system
      {
          // do something usafe
          return 2;
      }
 }

 auto doSomethingDumb(T)(ref T t)
 {
      T* pt = &t;
      return pt.doSomething();
 }

 auto s1 = S1();
 auto s2 = S2();
 auto x = doSomethingDumb(s1); // this call should be possible in  safe code
 auto y = doSomethingDumb(s2); // this should only be possible in  system
 code
I'm not sure if should mention it, but there is this little trick: ---- auto doSomethingDumb(T)(ref T t) { T* pt; () trusted { pt = &t; } (); /* Ok, because the reference is never returned. NOTE: DON'T RETURN THIS POINTER! */ return pt.doSomething(); } ---- Though in cases like this it's kind of an anti-pattern. The trusted code itself isn't actually safe, but the compiler thinks so. So you have to manually verify that doSomethingDumb is safe, even though it's not marked trusted. That's pretty bug-prone.
May 28 2016
prev sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 12:25:14 UTC, Lodovico Giaretta wrote:
 The problem is that T is a type, and I should check for safety 
 of every method of T that I'm using in my function. This does 
 not scale well, and if I change the body of the function to use 
 a new method, I may forget to add it to the isSafe checks.
I think i see what's going on then. Had to re-read it a few times. So ignore my previous reply. Easiest solution is to mark the entire struct as safe or trusted. Problem goes away (as long as you don't forcibly change it) Second is to force the check on the function before the call. static assert(isSafe!T.dosomething); pt.dosomething(); Third... You could put in safe code and have it complain? (might not work) safe { pt.dosomething(); //if not safe/ trusted it will refuse to compile } Fourth, you could create a helper function/template that cycles through a struct of your choice and tells you if any of it's methods fail to be safe. This will require a little more work, but it could be used as a full insurance and only requires a single template call on your function to ensure the safety. I can try and make this fourth one, but this isn't something I've done often.
May 28 2016
next sibling parent Lodovico Giaretta <lodovico giaretart.net> writes:
On Saturday, 28 May 2016 at 12:45:21 UTC, Era Scarecrow wrote:
 On Saturday, 28 May 2016 at 12:25:14 UTC, Lodovico Giaretta 
 wrote:
 The problem is that T is a type, and I should check for safety 
 of every method of T that I'm using in my function. This does 
 not scale well, and if I change the body of the function to 
 use a new method, I may forget to add it to the isSafe checks.
I think i see what's going on then. Had to re-read it a few times. So ignore my previous reply. Easiest solution is to mark the entire struct as safe or trusted. Problem goes away (as long as you don't forcibly change it) Second is to force the check on the function before the call. static assert(isSafe!T.dosomething); pt.dosomething(); Third... You could put in safe code and have it complain? (might not work) safe { pt.dosomething(); //if not safe/ trusted it will refuse to compile } Fourth, you could create a helper function/template that cycles through a struct of your choice and tells you if any of it's methods fail to be safe. This will require a little more work, but it could be used as a full insurance and only requires a single template call on your function to ensure the safety. I can try and make this fourth one, but this isn't something I've done often.
Thank you for your answer. I guess I'll got with overloading and if: auto doSomethingDumb(T)(ref T t) trusted if (isSafe!(T.doSomething)) { } auto doSomethingDumb(T)(ref T t) system if (!isSafe!(T.doSomething)) { } The only annoyance is this: auto doSomethingInRealLife(T)(T t) trusted if (isSafe!(T.doSomething1) && isSafe!(T.doSomething2) && isSafe!(T.doSomething3) ... and so on ... { } auto doSomethingInRealLife(T)(T t) system if (!isSafe!(T.doSomething1) || !isSafe!(T.doSomething2) || !isSafe!(T.doSomething3) ... and so on ... { }
May 28 2016
prev sibling parent Lodovico Giaretta <lodovico giaretart.net> writes:
On Saturday, 28 May 2016 at 12:45:21 UTC, Era Scarecrow wrote:
  Fourth, you could create a helper function/template that 
 cycles through a struct of your choice and tells you if any of 
 it's methods fail to be safe. This will require a little more 
 work, but it could be used as a full insurance and only 
 requires a single template call on your function to ensure the 
 safety.

  I can try and make this fourth one, but this isn't something 
 I've done often.
Thank you. This solution has the advantage of not bloating the code too much, but the drawback that it forces all methods of the template argument to be at least trusted, and not only the methods that my function needs to use, so imposing a stricter-than-necessary limit on its use in safe code.
May 28 2016
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 28 May 2016 at 11:50:33 UTC, Lodovico Giaretta wrote:
 Let's say I have a generic function that uses pointers. It will 
 be inferred  system by the compiler, but I know that the 
 pointer usage can be  trusted.
What kind of pointer usage do you have? Remember that basic & and * operations ARE safe. If you have more internally, you might be able to wrap them up in an trusted function to again allow inference to work.
May 28 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Saturday, 28 May 2016 at 13:03:10 UTC, Adam D. Ruppe wrote:
 What kind of pointer usage do you have? Remember that basic & 
 and * operations ARE  safe.

 If you have more internally, you might be able to wrap them up 
 in an  trusted function to again allow inference to work.
Ouch! I was under the impression that any pointer usage was forbidden in safe code. I have some structures holding pointers to other structures, and I'd like all of this structures being usable in safe code. The problem of some structures being deallocated while others are holding pointers to them cannot exist in my scenario, so I'm sure that my code can be trusted. The only problem is that these structures are parameterized, and the type parameters may have unsafe operations that I use.
May 28 2016
next sibling parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 13:10:56 UTC, Lodovico Giaretta wrote:
 On Saturday, 28 May 2016 at 13:03:10 UTC, Adam D. Ruppe wrote:
 What kind of pointer usage do you have? Remember that basic & 
 and * operations ARE  safe.

 If you have more internally, you might be able to wrap them up 
 in an  trusted function to again allow inference to work.
Ouch! I was under the impression that any pointer usage was forbidden in safe code.
Pointers by themselves are safe (as they have to point to something valid to start with); It's manipulating pointers that's usually unsafe (and the easiest source of bugs with pointers involved).
May 28 2016
prev sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 13:10:56 UTC, Lodovico Giaretta wrote:
 The only problem is that these structures are parameterized, 
 and the type parameters may have unsafe operations that I use.
Do you still want the template i'm building? It doesn't like stack frame pointers, but will work with regular structs. I'm still new to using traits myself so why it's getting me i don't know.
May 28 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Saturday, 28 May 2016 at 14:01:35 UTC, Era Scarecrow wrote:
 On Saturday, 28 May 2016 at 13:10:56 UTC, Lodovico Giaretta 
 wrote:
 The only problem is that these structures are parameterized, 
 and the type parameters may have unsafe operations that I use.
Do you still want the template i'm building? It doesn't like stack frame pointers, but will work with regular structs. I'm still new to using traits myself so why it's getting me i don't know.
Thank you very much for your effort. Please if you don't need it, don't make it, because I don't know if I'll use it. If I'll decide to go with it, I'll make it myself. I don't want to waste your time in something I may not use. Sorry for using your time, and thank you again.
May 28 2016
next sibling parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 14:11:56 UTC, Lodovico Giaretta wrote:
 On Saturday, 28 May 2016 at 14:01:35 UTC, Era Scarecrow wrote:
  Do you still want the template i'm building?
Thank you very much for your effort. Please if you don't need it, don't make it, because I don't know if I'll use it. If I'll decide to go with it, I'll make it myself. I don't want to waste your time in something I may not use. Sorry for using your time, and thank you again.
It's very much a learning experience. I'll tinker with this more.
May 28 2016
prev sibling parent reply Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 14:11:56 UTC, Lodovico Giaretta wrote:
 On Saturday, 28 May 2016 at 14:01:35 UTC, Era Scarecrow wrote:
  Do you still want the template i'm building?
Thank you very much for your effort. Please if you don't need it, don't make it, because I don't know if I'll use it.
Well here's what i got. Maybe someone else will tell me how i did this wrong... [code] import std.traits; template areAllFunctionsSafe(T) if (!isNested!T) { //nested may be lifted later when i fix this enum areAllFunctionsSafe = check(); bool check() { foreach(member; __traits(allMembers, T)) { static if(isCallable!(__traits(getMember, T, member))) { if (!isSafe!(__traits(getMember, T, member))) return false; } } return true; } } unittest { static struct S { int y; void foo() safe {} void bar() safe {} } static struct S2 { int y; void foo() safe {} void bar() {} } assert(areAllFunctionsSafe!S); assert(!areAllFunctionsSafe!S2); } [/code]
May 28 2016
parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Saturday, 28 May 2016 at 14:54:13 UTC, Era Scarecrow wrote:
  Well here's what i got. Maybe someone else will tell me how i 
 did this wrong...
Using the pragma to output how the lines were being generated i finally figured out why it kept complaining about the stack pointer and 'this'. So adding in a simple check to ignore 'this' entries. So, final version: bool areAllFunctionsSafe(T)() { foreach(member; __traits(allMembers, T)) { static if(member!="this" && isCallable!(__traits(getMember, T, member))) if (!isSafe!(__traits(getMember, T, member))) return false; } return true; }
May 28 2016
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/28/16 7:50 AM, Lodovico Giaretta wrote:
 Let's say I have a generic function that uses pointers. It will be
 inferred  system by the compiler, but I know that the pointer usage can
 be  trusted.
 The problem is that if I declare the function  trusted, I'm also
 implicitly trusting any call to  system methods of the template parameter.

 Dumb example (pointer usage can be trusted, but doSomething may not):

     auto doSomethingDumb(T)(ref T t) // I want it  trusted if
 doSomething is at least  trusted
     {
         T* pt = &t;
         return pt.doSomething();
     }

 Is there any way around this? Any way to declare a function  trusted as
 long as the methods of the template argument are at least  trusted?

 Thank you in advance.
You can create a trusted expression by using a lambda and immediately calling it. ag0aep6g brought it up. I would write it like this (untested, but I think this works): return (() trusted => &t)().doSomething(); The key is to limit your code that is tainted by trusted to as little code as possible. Note that doSomethingDumb will be inferred safe and not trusted. The compiler should NEVER infer trusted (for obvious reasons). -Steve
May 29 2016
parent Lodovico Giaretta <lodovico giaretart.net> writes:
On Sunday, 29 May 2016 at 18:02:53 UTC, Steven Schveighoffer 
wrote:
 You can create a trusted expression by using a lambda and 
 immediately calling it. ag0aep6g brought it up.

 I would write it like this (untested, but I think this works):

 return (() trusted => &t)().doSomething();

 The key is to limit your code that is tainted by  trusted to as 
 little code as possible.
This does indeed solve the problem without replicating code and costraints. It allows to just trust the parts of the function that can be trusted. Thank you very much. Lodovico Giaretta
May 31 2016