Re: why is ifThrown un@safe?

2019-03-15 Thread Bastiaan Veelo via Digitalmars-d-learn

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


Re: why is ifThrown un@safe?

2019-03-15 Thread Bastiaan Veelo via Digitalmars-d-learn

On Friday, 15 March 2019 at 19:19:41 UTC, H. S. Teoh wrote:
On Fri, Mar 15, 2019 at 06:46:25PM +, 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.


Re: why is ifThrown un@safe?

2019-03-15 Thread Bastiaan Veelo via Digitalmars-d-learn

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.




Re: why is ifThrown un@safe?

2019-03-15 Thread H. S. Teoh via Digitalmars-d-learn
On Fri, Mar 15, 2019 at 06:46:25PM +, 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


Re: why is ifThrown un@safe?

2019-03-15 Thread bauss via Digitalmars-d-learn

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.