Skip to content

Allow oc on const(Optional!X), allow frontOr within const nothrow @nogc #57

@SimonN

Description

@SimonN

Using optional 1.3.0.

The following code works with no problems.

import optional;

class X {
    int foo() const pure nothrow @safe @nogc {
        return 5;
    }
}

class Y {
    Optional!X x;

    int bar() pure @safe {
        return x.oc.foo.frontOr(3);
    }
}

This compiles fine.

The problem arises when we want to annotate bar() not only as pure @safe, but as const pure nothrow @safe @nogc in the same way as we did with foo(). If we add all those annotations to bar(), we get these errors:

  1. We can't call .oc.foo on a const Optional!X.
  2. We can't call frontOr within nothrow or @nogc, or on a const OptionalChain!int. The exact static assert is: "Unable to call frontOr on type const(OptionalChain!int). It has to either be an input range, a Nullable!T, or an Optional!T".

In theory, i.e., without looking at the implementation of the optional library, these extra annotations comply with D's type system:

  1. We don't modify x by maybe calling the const method foo() on (presumably) a const(X) through the const(Optional!X).
  2. We don't modify x.oc.foo by calling .frontOr(3) because we merely have to examine the range x.oc.foo for emptiness and possibly take its front element. We never have to advance this range. Only if we were to call .popFront on a range, the range struct would have to be mutable, but .frontOr(3) should never call .popFront on any range.
  3. We provide a default value for frontOr without calling anything that throws, therefore frontOr will never throw.
  4. Since frontOr never throws, it will never allocate with the GC.

We can argue that this report should be split into two separate issues: 1. oc should work on a const(Optional!X) and 2. frontOr should infer that it will neither modify, throw, nor allocate, given its usage with a default value like in our example. But I filed them together because they're so closely related, and because above example code is more meaningful when it shows both.

Do you see conceptual difficulties in allowing x.oc.foo.frontOr(3) from within a method annotated const pure nothrow @safe @nogc?

For now, I've rewritten the example as:

int bar() const pure nothrow @safe @nogc {
    return x.empty ? 3 : x.front.foo;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions