OpenD update - better stack traces, abstract class bug fixed, arsd 12 released

Posted 2025-05-26

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.

More...

OpenD recent changes

OpenD is continuing to focus on fixing bugs and other little quality of life decisions to make daily development a bit easier.

  • -checkaction=context is now default, use -checkaction=D to go back to the old default. This shows more relevant information in several common cases
  • -unittest=explicit is no longer marked experimental. This lets you run only the tests for modules you explicitly specify in the build, so you can focus faster on just the thing you're working on.
  • -g is now passed by default with the opend standard command.
  • The default stack trace printer tries to abbreviate irrelevant information. https://github.com/opendlang/opend/commit/da79e82c6507b81798c2de73c7d8ff47397e8914

    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.)

  • Error will call destructors as it unwinds the stack. We want to make null pointer checks throw by default and this change will help ensure that if we do that, you won't have random inconsistent objects as a result. https://github.com/opendlang/opend/commit/40e11759a82175b2f5edfe855c53a75ce88f176c
  • Years-old upstream bug fixed: abstract classes that leave interface methods unimplemented now correctly error at compile time instead of crashing with a null function at runtime https://github.com/opendlang/opend/commit/0b093770a0c52077f3b04d14e03d6c55fdf9bf1a (yes i know the commit message is horrible, i forgot to commit the bug fix before doing a ci experiment. but it too late to change now. oops and follow up https://github.com/opendlang/commit/1e758035744fde49425435dc44d920db22b6be3a)
    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.

  • OpenD specific bug fix: implicit construction now works in UDAs (https://github.com/opendlang/opend/commit/f9af3ac42a95e34eb23d7fd2f1752febf7808c84)
  • OpenD specific bug fix: building programs on Windows now work with dmd and ldc with and without Visual Studio installed, out of the box.

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.

arsd recent changes

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.cli has been added as a preview. It can automatically call functions with parameters from command line arguments.
  • arsd.pixmappaint and arsd.pixmaprecorder have been promoted from preview and are now formally supported.
  • arsd.jpeg can now read EXIF data, and will attempt to rotate the image buffer to match the orientation tag.
  • arsd.conv, arsd.string, arsd.uri, arsd.file, and arsd.uda provide public interfaces to existing arsd.core functionality.
  • arsd.random provides some little prng functions for simple games.
  • arsd.ini provides support for working with a variety of ini file format variations.
  • arsd.rtf, arsd.docx, arsd.pptx, arsd.xlsx provide support for reading those common file formats. RTF and xlsx are reasonably usable, but docx and ppts only pull the plain text out of the files at this time.

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.