www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - why is ifThrown un safe?

reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
In the code below (https://run.dlang.io/is/d0oTNi), ifThrown is 
inferred as un safe. If instead I write the implementation of 
ifThrown out (after res2) then it is  safe. As far as I can see, 
there is no real difference. So why doesn't ifThrown work in this 
case, and can it be made to work?

Thanks!

void main()  safe
{
     import std.process;
     import std.exception;

      const res1 = execute(["clang", "-v", "-xc++", "/dev/null", 
"-fsyntax-only"], ["LANG": "C"])
         .ifThrown((e)  safe {
             import std.typecons : Tuple;
             return Tuple!(int, "status", string, "output")(-1, 
e.msg);
         }); // Fails

     const res2 = () {
         try
         {
             return execute(["clang", "-v", "-xc++", "/dev/null", 
"-fsyntax-only"], ["LANG": "C"]);
         }
         catch (Exception e)
         {
             import std.typecons : Tuple;
             return Tuple!(int, "status", string, "output")(-1, 
e.msg);
         }
     }();
}
Mar 15
parent reply bauss <jj_1337 live.dk> writes:
On Friday, 15 March 2019 at 18:04:05 UTC, Bastiaan Veelo wrote:
 In the code below (https://run.dlang.io/is/d0oTNi), ifThrown is 
 inferred as un safe. If instead I write the implementation of 
 ifThrown out (after res2) then it is  safe. As far as I can 
 see, there is no real difference. So why doesn't ifThrown work 
 in this case, and can it be made to work?

 Thanks!

 void main()  safe
 {
     import std.process;
     import std.exception;

      const res1 = execute(["clang", "-v", "-xc++", "/dev/null", 
 "-fsyntax-only"], ["LANG": "C"])
         .ifThrown((e)  safe {
             import std.typecons : Tuple;
             return Tuple!(int, "status", string, "output")(-1, 
 e.msg);
         }); // Fails

     const res2 = () {
         try
         {
             return execute(["clang", "-v", "-xc++", 
 "/dev/null", "-fsyntax-only"], ["LANG": "C"]);
         }
         catch (Exception e)
         {
             import std.typecons : Tuple;
             return Tuple!(int, "status", string, "output")(-1, 
 e.msg);
         }
     }();
 }
Because the handlers may be unsafe. There is no safe overload of ifThrown. However you can work around this using trusted.
Mar 15
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Fri, Mar 15, 2019 at 06:46:25PM +0000, bauss via Digitalmars-d-learn wrote:
 On Friday, 15 March 2019 at 18:04:05 UTC, Bastiaan Veelo wrote:
 In the code below (https://run.dlang.io/is/d0oTNi), ifThrown is
 inferred as un safe. If instead I write the implementation of
 ifThrown out (after res2) then it is  safe. As far as I can see,
 there is no real difference. So why doesn't ifThrown work in this
 case, and can it be made to work?
[...]
 Because the handlers may be unsafe.
 
 There is no safe overload of ifThrown.
 
 However you can work around this using  trusted.
I wasn't satisfied with this answer, because in theory the safe-ness of ifThrown ought to be inferred from the safe-ness of its arguments, and ifThrown itself shouldn't do anything un- safe. So I investigated a little further, and found that the problem lies in how ifThrown is declared: CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate(Exception) errorHandler) { ... } The problem is that the second parameter is declared to be a delegate with no further qualifications, which means it defaults to system. Therefore, even if `expression` and `errorHandler` are both safe, the compiler will still infer the call to `errorHandler` as system, and therefore ifThrown will also be inferred as system. The obvious fix of adding safe to the second parameter won't work, because that would preclude ifThrown from being used with system error handlers. So it appears to me that in order to make this work as it should, we need to templatize not only on the return type of the delegate, but on the delegate type itself. Perhaps something along the lines of: CommonType!(T1, ErrorHandler) ifThrown(T1, T2)(lazy scope T1 expression, scope ErrorHandler errorHandler) if (... && is(ErrorHandler == delegate) && is(ReturnType!ErrorHandler : T1)) { ... } This should probably be filed as an enhancement request in bugzilla. T -- I am not young enough to know everything. -- Oscar Wilde
Mar 15
parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Friday, 15 March 2019 at 19:19:41 UTC, H. S. Teoh wrote:
 On Fri, Mar 15, 2019 at 06:46:25PM +0000, bauss via 
 Digitalmars-d-learn wrote:
 On Friday, 15 March 2019 at 18:04:05 UTC, Bastiaan Veelo wrote:
 In the code below (https://run.dlang.io/is/d0oTNi), ifThrown 
 is inferred as un safe. If instead I write the 
 implementation of ifThrown out (after res2) then it is 
  safe. As far as I can see, there is no real difference. So 
 why doesn't ifThrown work in this case, and can it be made 
 to work?
[...]
 Because the handlers may be unsafe.
 
 There is no safe overload of ifThrown.
 
 However you can work around this using  trusted.
I wasn't satisfied with this answer, because in theory the safe-ness of ifThrown ought to be inferred from the safe-ness of its arguments, and ifThrown itself shouldn't do anything un- safe. So I investigated a little further, and found that the problem lies in how ifThrown is declared: CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate(Exception) errorHandler) { ... } The problem is that the second parameter is declared to be a delegate with no further qualifications, which means it defaults to system. Therefore, even if `expression` and `errorHandler` are both safe, the compiler will still infer the call to `errorHandler` as system, and therefore ifThrown will also be inferred as system. The obvious fix of adding safe to the second parameter won't work, because that would preclude ifThrown from being used with system error handlers. So it appears to me that in order to make this work as it should, we need to templatize not only on the return type of the delegate, but on the delegate type itself. Perhaps something along the lines of: CommonType!(T1, ErrorHandler) ifThrown(T1, T2)(lazy scope T1 expression, scope ErrorHandler errorHandler) if (... && is(ErrorHandler == delegate) && is(ReturnType!ErrorHandler : T1)) { ... } This should probably be filed as an enhancement request in bugzilla. T
Excellent, thank you. Will do the filing and maybe experiment a bit. Bastiaan.
Mar 15
parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Friday, 15 March 2019 at 19:24:17 UTC, Bastiaan Veelo wrote:
 Will do the filing and maybe experiment a bit.

 Bastiaan.
https://issues.dlang.org/show_bug.cgi?id=19741
Mar 15
prev sibling parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Friday, 15 March 2019 at 18:46:25 UTC, bauss wrote:
 On Friday, 15 March 2019 at 18:04:05 UTC, Bastiaan Veelo wrote:
 In the code below (https://run.dlang.io/is/d0oTNi), ifThrown 
 is inferred as un safe. If instead I write the implementation 
 of ifThrown out (after res2) then it is  safe. As far as I can 
 see, there is no real difference. So why doesn't ifThrown work 
 in this case, and can it be made to work?

 Thanks!

 void main()  safe
 {
     import std.process;
     import std.exception;

      const res1 = execute(["clang", "-v", "-xc++", 
 "/dev/null", "-fsyntax-only"], ["LANG": "C"])
         .ifThrown((e)  safe {
             import std.typecons : Tuple;
             return Tuple!(int, "status", string, "output")(-1, 
 e.msg);
         }); // Fails

     const res2 = () {
         try
         {
             return execute(["clang", "-v", "-xc++", 
 "/dev/null", "-fsyntax-only"], ["LANG": "C"]);
         }
         catch (Exception e)
         {
             import std.typecons : Tuple;
             return Tuple!(int, "status", string, "output")(-1, 
 e.msg);
         }
     }();
 }
Because the handlers may be unsafe.
But this handler is explicitly marked safe, and ifThrown is a template, which should infer its attributes, right?
 There is no safe overload of ifThrown.
Could it be added?
 However you can work around this using  trusted.
I know, but since everything really is safe, I'd rather not imply that it might not be. Besides, I failed to use trusted without introducing a new scope, so I'd have to mark the whole block where `res1` is used as trusted. Maybe I did that wrong though.
Mar 15