OpenD is continuing to focus on fixing bugs and other little quality of life decisions to make daily development a bit easier.
This collapses select library internals to just the interface seen in user code, abbreviates immutable(char)[] to string, skips irrelevant attributes to identifying the chosen overload, indents each line to make it easier to visually skim, and stops printing at the main druntime boundaries (in unit tests; it already did in normal main() functions).
For normal exceptions:
Before:
snip ---------------- /home/me/opend/opend-latest-linux-x86_64/bin/../import/std/conv.d:2571 pure @safe int std.conv.parse!(int, immutable(char)[], 0).parse(scope ref immutable(char)[]) [0x44a514] /home/me/opend/opend-latest-linux-x86_64/bin/../import/std/conv.d:2036 pure @safe int std.conv.toImpl!(int, immutable(char)[]).toImpl(immutable(char)[]) [0x44bef0] /home/me/opend/opend-latest-linux-x86_64/bin/../import/std/conv.d:210 pure @safe int std.conv.to!(int).to!(immutable(char)[]).to(immutable(char)[]) [0x4499e3] ./backtrace.d:4 pure @safe int backtrace.cool().__lambda_L4_C33!(immutable(char)[]).__lambda_L4_C33(immutable(char)[]) [0x4499bf] /home/me/opend/opend-latest-linux-x86_64/bin/../import/std/algorithm/iteration.d:768 pure @property @safe int std.algorithm.iteration.MapResult!(backtrace.cool().__lambda_L4_C33, immutable(char)[][]).MapResult.front() [0x44c0cb] ./backtrace.d:5 void backtrace.cool() [0x449799] ./backtrace.d:10 _Dmain [0x4497f8]
After:
snip ---------------- /home/me/opend/opend-latest-linux-x86_64/bin/../import/std/conv.d:210 std.conv.to!(int).to!(string).to(string) [0x4564ab] ./backtrace.d:4 backtrace.cool().__lambda1!(string).__lambda1(string) [0x441a7f] /home/me/opend/opend-latest-linux-x86_64/bin/../import/std/algorithm/iteration.d:582 std.algorithm.iteration.MapResult!(backtrace.cool().__lambda1, string[]).MapResult.front() [0x442ca3] ./backtrace.d:5 backtrace.cool() [0x441789] ./backtrace.d:10 _Dmain [0x4417e8]
I think there's still room for improvement, but I like this a lot more. Especially for unittests, where it is usually compressed to two lines instead of like twenty:
Before:
core.exception.AssertError@backtrace.d(15): unittest failure ---------------- ??:? _d_unittestp [0x468991] ??:? void foo.__unittest_L14_C1() [0x44980c] ??:? void foo.__modtest() [0x45e2e0] ??:? int core.runtime.runModuleUnitTests().__foreachbody_L600_C5(object.ModuleInfo*) [0x46fed2] ??:? int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)).__lambda_L2459_C13(immutable(object.ModuleInfo*)) [0x45fa6f] ??:? int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))).__foreachbody_L584_C5(ref rt.sections_elf_shared.DSO) [0x46bb5b] ??:? int rt.sections_elf_shared.DSO.opApply(scope int delegate(ref rt.sections_elf_shared.DSO)) [0x46bce5] ??:? int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))) [0x46bae9] ??:? int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)) [0x45fa41] ??:? runModuleUnitTests [0x46fd07] ??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).runAll() [0x469e34] ??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).tryExec(scope void delegate()) [0x469dc1] ??:? _d_run_main2 [0x469d2a] ??:? _d_run_main [0x469b13] ??:? main [0x449823] ??:? [0x7fddb5227af6] ??:? __libc_start_main [0x7fddb5227bb4] <unknown dir>/<unknown file>:115 _start [0x449650] 1/1 modules FAILED unittests
After (also note the effect of the new unittest=context default on the first line):
core.exception.AssertError@backtrace.d(15): 4 != 2 ---------------- ??:? _d_unittest_msg [0x444a3c] ./backtrace.d:15 foo.__unittest_L14_C1() [0x439848] 1/1 modules FAILED unittests
(there is a bizarre shortcut in druntime where if the module name matches the file name, it shortens things even more, skipping printing the trace entirely, both before and after, so if you see something shorter, try adding a module test.thing; to the top of the file then rerun the test to see the difference. Library modules almost always have an explicit module definition, so this shortcut is definitely insufficient.)
interface I { void method(); } abstract class A: I { } class C : A { } void main() { C c = new C; c.method(); }
This previously segfaulted at runtime, trying to call a virtual function through a null entry in the vtable. It now correctly issues an error at compile time that class C has an unimplemented interface method.
Future directions:
Upstream had some performance improvements from Vladimir we might want to port over.
static if is basically useless for defining module members, since it is order dependent. idk if fixing this is realistic at all though, it'd be a major overhaul, I think it is better to avoid it right now, but that torpedoes my plan to transition away from version.
The arsd library of modules just formally tagged version 12.0. It comes with some minor breaking changes for some minigui custom widgets, switches simpledisplay to use the new arsd.core message loop (which also brings better Mac support), and a lot of new things. It is the first formal tag for months, and thus includes things regular readers of this blog will have already heard of.
The font stuff has been refactored to allow float or int. It now defaults to float, but internally it uses MeasurableFont.fnum as an alias so I can switch it back if needed. This may cause some errors or warnings if you use them with int now.
The release includes several new modules, some of which are new functionality, some just really minimal features, and several public interfaces to internal arsd.core-based functionality.
arsd 13 is tentatively scheduled for release next May, in 2026. 12.1 may come at any time, but I'll probably try for some time in July. Of course, realistically, the version tag is not that important, since opend just bundles it on a rolling release anyway.
OpenD and the arsd extended library got more little things this month. arsd also continuing to transition toward a unified event loop and has improved Mac support.