diff --git a/dub.sdl b/dub.sdl index 73e63088..a9d48f32 100644 --- a/dub.sdl +++ b/dub.sdl @@ -52,3 +52,8 @@ configuration "libasync" { configuration "generic" { // Defines eventDriver as the generic EventDriver interface. Setup must be done manually. } + +buildType "unittest-dip1000" { + buildOptions "unittests" "debugMode" "debugInfo" + dflags "-preview=dip1000" +} diff --git a/run-ci.sh b/run-ci.sh index f2b1f448..398a9417 100755 --- a/run-ci.sh +++ b/run-ci.sh @@ -12,6 +12,8 @@ fi dub test --arch=$ARCH --compiler=$DC -c $CONFIG +dub test --arch=$ARCH --compiler=$DC -c $CONFIG -b unittest-dip1000 + if [ ${BUILD_EXAMPLE=1} -eq 1 ]; then for ex in $(\ls -1 examples/*.d); do echo "[INFO] Building example $ex" diff --git a/source/eventcore/drivers/posix/dns.d b/source/eventcore/drivers/posix/dns.d index c4db2f7a..f098b950 100644 --- a/source/eventcore/drivers/posix/dns.d +++ b/source/eventcore/drivers/posix/dns.d @@ -398,7 +398,7 @@ final class EventDriverDNS_GHBN(Events : EventDriverEvents, Signals : EventDrive scope addr = new RefAddress(() @trusted { return cast(sockaddr*)&sa; } (), sa.sizeof); RefAddress[1] aa; aa[0] = addr; - on_lookup_finished(handle, DNSStatus.ok, aa); + on_lookup_finished(handle, DNSStatus.ok, () @trusted { return aa[]; } ()); // NOTE DIP-1000 doesn't allow this, even if the parameter is scope } break; case AF_INET6: { sockaddr_in6 sa; @@ -407,7 +407,7 @@ final class EventDriverDNS_GHBN(Events : EventDriverEvents, Signals : EventDrive scope addr = new RefAddress(() @trusted { return cast(sockaddr*)&sa; } (), sa.sizeof); RefAddress[1] aa; aa[0] = addr; - on_lookup_finished(handle, DNSStatus.ok, aa); + on_lookup_finished(handle, DNSStatus.ok, () @trusted { return aa[]; } ()); // NOTE DIP-1000 doesn't allow this, even if the parameter is scope } break; } diff --git a/source/eventcore/drivers/posix/events.d b/source/eventcore/drivers/posix/events.d index c54fa6f7..5711b045 100644 --- a/source/eventcore/drivers/posix/events.d +++ b/source/eventcore/drivers/posix/events.d @@ -171,18 +171,24 @@ final class PosixEventDriverEvents(Loop : PosixEventLoop, Sockets : EventDriverS trigger(event, cnt > 0); } } else { - private void onSocketData(DatagramSocketFD s, IOStatus st, size_t, scope RefAddress) + private void onSocketData(DatagramSocketFD socket, IOStatus st, size_t, scope RefAddress) @nogc { // avoid infinite recursion in case of errors if (st == IOStatus.ok) - m_sockets.receiveNoGC(s, m_buf, IOMode.once, &onSocketData); + m_sockets.receiveNoGC(socket, m_buf, IOMode.once, &onSocketData); try { - EventID evt = m_sockets.userData!EventID(s); - scope doit = { - trigger(evt, (cast(long[])m_buf)[0] > 1); - }; // cast to nogc - () @trusted { (cast(void delegate() @nogc)doit)(); } (); + EventID evt = m_sockets.userData!EventID(socket); + // FIXME: push @nogc further down the call chain instead of + // performing this ugly workaround + static struct S { + PosixEventDriverEvents this_; + EventID evt; + bool all; + void doit() { this_.trigger(evt, all); } + } + scope s = S(this, evt, (cast(long[])m_buf)[0] > 1); + () @trusted { (cast(void delegate() @nogc)&s.doit)(); } (); } catch (Exception e) assert(false, e.msg); } } diff --git a/source/eventcore/drivers/posix/sockets.d b/source/eventcore/drivers/posix/sockets.d index 62ae312c..d62877d7 100644 --- a/source/eventcore/drivers/posix/sockets.d +++ b/source/eventcore/drivers/posix/sockets.d @@ -895,10 +895,19 @@ final class PosixEventDriverSockets(Loop : PosixEventLoop) : EventDriverSockets package void receiveNoGC(DatagramSocketFD socket, ubyte[] buffer, IOMode mode, void delegate(DatagramSocketFD, IOStatus, size_t, scope RefAddress) @safe nothrow @nogc on_receive_finish) @trusted @nogc { - scope void delegate() @safe nothrow do_it = { - receive(socket, buffer, mode, on_receive_finish); - }; - (cast(void delegate() @safe nothrow @nogc)do_it)(); + // FIXME: push @nogc further down the call chain instead of + // performing this ugly workaround + static struct S { + PosixEventDriverSockets this_; + DatagramSocketFD socket; + ubyte[] buffer; + IOMode mode; + void delegate(DatagramSocketFD, IOStatus, size_t, scope RefAddress) @safe nothrow @nogc on_receive_finish; + void doit() { this_.receive(socket, buffer, mode, on_receive_finish); } + + } + scope s = S(this, socket, buffer, mode, on_receive_finish); + (cast(void delegate() @safe nothrow @nogc)&s.doit)(); } void cancelReceive(DatagramSocketFD socket) diff --git a/source/eventcore/drivers/winapi/core.d b/source/eventcore/drivers/winapi/core.d index 49d627c3..4ac812b2 100644 --- a/source/eventcore/drivers/winapi/core.d +++ b/source/eventcore/drivers/winapi/core.d @@ -268,9 +268,13 @@ final class WinAPIEventDriverCore : EventDriverCore { } package void discardEvents(scope OVERLAPPED_CORE*[] overlapped...) -@nogc { - import std.algorithm.searching : canFind; - m_ioEvents.filterPending!(evt => !overlapped.canFind(evt.overlapped)); + @nogc { + m_ioEvents.filterPending!((evt) @safe { + foreach (ovl; overlapped) + if (ovl is evt.overlapped) + return false; + return true; + }); } private void executeThreadCallbacks() diff --git a/source/eventcore/drivers/winapi/dns.d b/source/eventcore/drivers/winapi/dns.d index 1071dd7d..73661294 100644 --- a/source/eventcore/drivers/winapi/dns.d +++ b/source/eventcore/drivers/winapi/dns.d @@ -33,7 +33,7 @@ final class WinAPIEventDriverDNS : EventDriverDNS { scope addr = new RefAddress(() @trusted { return cast(sockaddr*)&sa; } (), addrlen); RefAddress[1] addrs; addrs[0] = addr; - on_lookup_finished(id, DNSStatus.ok, addrs); + on_lookup_finished(id, DNSStatus.ok, () @trusted { return addrs[]; } ()); // NOTE DIP-1000 doesn't allow this, even if the parameter is scope return id; } diff --git a/source/eventcore/drivers/winapi/sockets.d b/source/eventcore/drivers/winapi/sockets.d index 732d19b0..2d0f22ae 100644 --- a/source/eventcore/drivers/winapi/sockets.d +++ b/source/eventcore/drivers/winapi/sockets.d @@ -600,7 +600,7 @@ final class WinAPIEventDriverSockets : EventDriverSockets { if (!isValid(socket)) return false; - switch (multicast_address.addressFamily) { + switch (() @trusted { return multicast_address.addressFamily; } ()) { // DIP-1000: addressFamily's this is not considered scope default: assert(false, "Multicast only supported for IPv4/IPv6 sockets."); case AddressFamily.INET: struct ip_mreq { diff --git a/source/eventcore/internal/consumablequeue.d b/source/eventcore/internal/consumablequeue.d index 6fd6a041..364f9be5 100644 --- a/source/eventcore/internal/consumablequeue.d +++ b/source/eventcore/internal/consumablequeue.d @@ -177,7 +177,7 @@ final class ConsumableQueue(T) m_first = m_first & m_capacityMask; } - private ref T getPendingAt(size_t idx) + private scope ref T getPendingAt(size_t idx) { assert(idx < m_pendingCount, "Pending item index out of bounds."); return m_storage[(m_first + m_consumedCount + idx) & m_capacityMask].value; @@ -251,7 +251,7 @@ unittest { void filterPending(alias pred, T)(ConsumableQueue!T q) -{ +@safe { size_t ir = 0; size_t iw = 0; diff --git a/source/eventcore/internal/utils.d b/source/eventcore/internal/utils.d index c4b72fef..edf2e70e 100644 --- a/source/eventcore/internal/utils.d +++ b/source/eventcore/internal/utils.d @@ -8,13 +8,23 @@ import taggedalgebraic; void print(ARGS...)(string str, ARGS args) @trusted @nogc nothrow { import std.format : formattedWrite; - StdoutRange r; - scope cb = () { - try (&r).formattedWrite(str, args); - catch (Exception e) assert(false, e.msg); - }; - (cast(void delegate() @nogc @safe nothrow)cb)(); - r.put('\n'); + // NOTE: formattedWrite isn't @nogc, although it could be for the types that + // we actually log. Since printing is only enabled in debug settings, + // @nogc is artificailly enforced here to be able to provice a @nogc + // API. + static struct S { + string str; + ARGS args; + StdoutRange r; + void doit() + { + try (&r).formattedWrite(str, args); + catch (Exception e) assert(false, e.msg); + } + } + scope s = S(str, args); + (cast(void delegate() @nogc @safe nothrow)&s.doit)(); + s.r.put('\n'); } T mallocT(T, ARGS...)(ARGS args) @@ -75,10 +85,14 @@ void freeNT(T)(ref T[] arr) } private void noGCDestroy(T)(ref T t) -@trusted { +@trusted @nogc { // FIXME: only do this if the destructor chain is actually nogc - scope doit = { destroy(t); }; - (cast(void delegate() @nogc)doit)(); + static struct S { + T* pt; + void doit() { destroy(*pt); } + } + scope s = S(&t); + (cast(void delegate() @nogc)&s.doit)(); } private extern(C) Throwable.TraceInfo _d_traceContext(void* ptr = null); @@ -94,15 +108,27 @@ void nogc_assert(bool cond, string message, string file = __FILE__, int line = _ assert(false); } - scope doit = { - stderr.writefln("Assertion failure @%s(%s): %s", file, line, message); - stderr.writeln("------------------------"); - if (auto info = _d_traceContext(null)) { - foreach (s; info) - stderr.writeln(s); - } else stderr.writeln("no stack trace available"); - }; - (cast(void delegate() @nogc)doit)(); // write and _d_traceContext are not nogc + // NOTE: writefln isn't @nogc, although it could be for the types that + // we actually log. Since this is only called right before app + // termination, enforcing @nogc is done here to allow keeping the + // API @nogc. + static struct S { + string message; + string file; + int line; + void doit() + { + stderr.writefln("Assertion failure @%s(%s): %s", file, line, message); + stderr.writeln("------------------------"); + if (auto info = _d_traceContext(null)) { + foreach (s; info) + stderr.writeln(s); + } else stderr.writeln("no stack trace available"); + } + + } + scope s = S(message, file, line); + (cast(void delegate() @nogc)&s.doit)(); // write and _d_traceContext are not nogc } }