On Thu, Jun 19, 2014 at 12:59:00PM -0700, Walter Bright via Digitalmars-d wrote: > With nothrow and @nogc annotations, we've been motivated to add these > annotations to C system API functions, because obviously such > functions aren't going to throw D exceptions or call the D garbage > collector. > > But this exposed a problem - functions like C's qsort() take a pointer > to a callback function. The callback function, being supplied by the D > programmer, may throw and may call the garbage collector. By requiring > the callback function to be also nothrow @nogc, this is an > unreasonable requirement besides breaking most existing D code that > uses qsort(). > > This problem applies as well to the Windows APIs and the Posix APIs > with callbacks. > > The solution is to use overloading so that if your callback is > nothrow, it will call the nothrow version of qsort, if it is > throwable, it calls the throwable version of qsort. > > Never mind that those two versions of qsort are actually the same > function (!), even though D's type system regards them as different. > Although this looks like an usafe hack, it actually is quite safe, > presuming that the rest of the qsort code itself does not throw. This > technique relies on the fact that extern(C) functions do not get their > types mangled into the names. > > Some example code: > > extern (C) { alias int function() fp_t; } > extern (C) nothrow { alias int function() fpnothrow_t; } > > extern (C) int foo(int a, fp_t fp); > extern (C) nothrow int foo(int a, fpnothrow_t fp); > > extern (C) int bar(); > extern (C) nothrow int barnothrow(); > > void test() { > foo(1, &bar); // calls the 'throwing' foo() > foo(1, &barnothrow); // calls the 'nothrow' foo() > }
This is a clever hack to work around the type system, but it introduces boilerplate, and doesn't ultimately fix the underlying problem. I agree that it's "good enough" for now -- to get those system APIs working without massive breakage of existing code -- but I think for the long run, we should face the root issue: functions that call callbacks have attributes that *depend* on an input argument. So we really should have the equivalent of "inout" for other attributes than const. For example, a function can be "dependently nothrow", meaning that the body of the function is nothrow, except for that call to user-supplied delegate: // This is hypothetical syntax. int dgCaller(scope int delegate(int) dg inout(nothrow)) inout(nothrow) { //throw new Exception(...); // illegal: body of function must not throw int result = dg(1); // OK: we inherit nothrow-ness from dg(). return result; } void f1() nothrow { // OK: the delegate is nothrow, so dgCaller is nothrow, // so it's permitted to call it from a nothrow function. auto x = dgCaller((x) => x+1); } void f2() { // OK: the delegate is throwing, which makes dgCaller // nothrowing w.r.t. this call. So this function becomes // throwing. auto x = dgCaller((int) { throw new Exception(...); }); } void f3() nothrow { // ILLEGAL: the delegate throws, so dgCaller may throw, // so we can't call it from a nothrow function. auto x = dgCaller((int) { throw new Exception(...); }); } This capability will solve a lot of attribute-related issues especially in generic code. For example, a container that implements opApply() currently cannot be marked pure, because it would impose purity on the delegate passed to it, which greatly limits its applicability. But accepting a non-pure delegate makes it uncallable from pure code, even if opApply() itself doesn't do anything impure. So we end up needing to write two identical versions of opApply: one pure, and one non-pure. And this has to be multiplied for each attribute we wish to support (pure, nothrow, @safe, @nogc -- that's 2^4 = 16 copies of the same function), which is clearly untenable. With inout(nothrow), inout(pure), etc., we can collapse all of these variants into a single function, and at the same time have the compiler statically verify that the result does not violate any attribute. T -- "I'm not childish; I'm just in touch with the child within!" - RL