Thread overview
why is ifThrown un@safe?
Mar 15
bauss
March 15
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);
        }
    }();
}

March 15
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.
March 15
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
March 15
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.

March 15
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.
March 15
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