digitalmars.D.learn - trusting generic functions
- Lodovico Giaretta (19/19) May 28 2016 Let's say I have a generic function that uses pointers. It will
- Era Scarecrow (5/9) May 28 2016 Use traits..
- Era Scarecrow (7/8) May 28 2016 Should also probably test for a function or delegate. So...?
- Lodovico Giaretta (5/9) May 28 2016 The problem is that T is a type, and I should check for safety of
- Era Scarecrow (6/12) May 28 2016 Hmmm that doesn't sound right. Safe code can't call unsafe code
- Lodovico Giaretta (29/43) May 28 2016 Sorry for not being clear. Look at this example:
- ag0aep6g (15/41) May 28 2016 I'm not sure if should mention it, but there is this little trick:
- Era Scarecrow (22/26) May 28 2016 I think i see what's going on then. Had to re-read it a few
- Lodovico Giaretta (26/54) May 28 2016 Thank you for your answer.
- Lodovico Giaretta (7/15) May 28 2016 Thank you.
- Adam D. Ruppe (5/8) May 28 2016 What kind of pointer usage do you have? Remember that basic & and
- Lodovico Giaretta (10/14) May 28 2016 Ouch! I was under the impression that any pointer usage was
- Era Scarecrow (5/13) May 28 2016 Pointers by themselves are safe (as they have to point to
- Era Scarecrow (5/7) May 28 2016 Do you still want the template i'm building? It doesn't like
- Lodovico Giaretta (7/15) May 28 2016 Thank you very much for your effort.
- Era Scarecrow (2/10) May 28 2016 It's very much a learning experience. I'll tinker with this more.
- Era Scarecrow (33/38) May 28 2016 Well here's what i got. Maybe someone else will tell me how i
- Era Scarecrow (14/16) May 28 2016 Using the pragma to output how the lines were being generated i
- Steven Schveighoffer (10/25) May 29 2016 You can create a trusted expression by using a lambda and immediately
- Lodovico Giaretta (8/14) May 31 2016 This does indeed solve the problem without replicating code and
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
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
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
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
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: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?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
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: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 codeOn Saturday, 28 May 2016 at 11:57:09 UTC, Era Scarecrow wrote: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?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
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 codeI'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
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
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: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 ... { }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
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
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
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
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: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).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.
May 28 2016
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
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: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.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
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:It's very much a learning experience. I'll tinker with this more.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.
May 28 2016
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: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]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.
May 28 2016
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
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
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