digitalmars.D.learn - why is ifThrown un safe?
- Bastiaan Veelo (31/31) Mar 15 2019 In the code below (https://run.dlang.io/is/d0oTNi), ifThrown is
- bauss (4/35) Mar 15 2019 Because the handlers may be unsafe.
- H. S. Teoh (29/40) Mar 15 2019 I wasn't satisfied with this answer, because in theory the @safe-ness of
- Bastiaan Veelo (4/49) Mar 15 2019 Excellent, thank you. Will do the filing and maybe experiment a
- Bastiaan Veelo (2/4) Mar 15 2019 https://issues.dlang.org/show_bug.cgi?id=19741
- Bastiaan Veelo (8/47) Mar 15 2019 But this handler is explicitly marked @safe, and ifThrown is a
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 2019
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 2019
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 2019
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:Excellent, thank you. Will do the filing and maybe experiment a bit. Bastiaan.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
Mar 15 2019
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 2019
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:But this handler is explicitly marked safe, and ifThrown is a template, which should infer its attributes, right?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.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 2019